-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
using AEMO.MDFF.Abstractions; | ||
|
||
namespace AEMO.MDFF; | ||
|
||
public sealed class EndRecord : IMdffRecord | ||
{ | ||
public string RecordIndicator => "900"; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using AEMO.MDFF.Abstractions; | ||
|
||
namespace AEMO.MDFF; | ||
|
||
public sealed class HeaderRecord : IMdffRecord | ||
{ | ||
public string RecordIndicator => "100"; | ||
public string VersionHeader { get; set; } | ||
public DateTime DateTime { get; set; } | ||
public string FromParticipant { get; set; } | ||
public string ToParticipant { get; set; } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
using AEMO.MDFF.Abstractions; | ||
|
||
namespace AEMO.MDFF.NEM12; | ||
|
||
public sealed class IntervalDataRecord : IMdffRecord | ||
{ | ||
public string RecordIndicator => "300"; | ||
public DateOnly IntervalDate { get; set; } | ||
public IReadOnlyCollection<decimal> IntervalValues { get; set; } | ||
Check warning on line 9 in src/AEMO.MDFF/NEM12/IntervalDataRecord.cs
|
||
public string QualityMethod { get; set; } | ||
public string ReasonCode { get; set; } | ||
public string ReasonDescription { get; set; } | ||
public string UpdateDateTime { get; set; } | ||
public DateTime MSATSLoadDateTime { get; set; } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using AEMO.MDFF.Abstractions; | ||
|
||
namespace AEMO.MDFF.NEM12; | ||
|
||
public sealed class NMIDataDetailsRecord : IMdffRecord | ||
{ | ||
public string RecordIndicator => "200"; | ||
public required string NMI { get; set; } | ||
public string NMIConfiguration { get; set; } | ||
Check warning on line 9 in src/AEMO.MDFF/NEM12/NMIDataDetailsRecord.cs
|
||
public string RegisterId { get; set; } | ||
Check warning on line 10 in src/AEMO.MDFF/NEM12/NMIDataDetailsRecord.cs
|
||
public string NMISuffix { get; set; } | ||
Check warning on line 11 in src/AEMO.MDFF/NEM12/NMIDataDetailsRecord.cs
|
||
public string MDMDataStreamIdentifier { get; set; } | ||
Check warning on line 12 in src/AEMO.MDFF/NEM12/NMIDataDetailsRecord.cs
|
||
public string MeterSerialNumber { get; set; } | ||
Check warning on line 13 in src/AEMO.MDFF/NEM12/NMIDataDetailsRecord.cs
|
||
public string UOM { get; set; } | ||
Check warning on line 14 in src/AEMO.MDFF/NEM12/NMIDataDetailsRecord.cs
|
||
public int IntervalLength { get; set; } | ||
public DateOnly NextScheduledReadDate { get; set; } | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
using System.Globalization; | ||
using System.Runtime.CompilerServices; | ||
using AEMO.MDFF.Abstractions; | ||
using Sylvan.Data.Csv; | ||
|
||
namespace AEMO.MDFF.NEM12; | ||
|
||
public class Nem12Reader() : IMdffReader | ||
{ | ||
private readonly Dictionary<string, int> _nmiIntervalLengths = new(); | ||
|
||
public async IAsyncEnumerable<IMdffRecord> ReadAsync(Stream stream, [EnumeratorCancellation] CancellationToken ct) | ||
{ | ||
using var sr = new StreamReader(stream); | ||
await using var csv = await CsvDataReader.CreateAsync(sr, | ||
new CsvDataReaderOptions() | ||
{ | ||
HasHeaders = false, | ||
}, ct); | ||
|
||
bool headerFound = false; | ||
bool endFound = false; | ||
string currentNMI = null; | ||
|
||
while (await csv.ReadAsync(ct) && !endFound) | ||
{ | ||
var recordIndicator = csv.GetString(0); | ||
switch (recordIndicator) | ||
{ | ||
case "100": | ||
if (headerFound) | ||
throw new InvalidDataException("Multiple header records found"); | ||
var hr = ParseHeaderRecord(csv); | ||
headerFound = true; | ||
yield return hr; | ||
break; | ||
case "200": | ||
if (!headerFound) | ||
throw new InvalidDataException("Data record found before header"); | ||
var ddr = ParseNMIDataDetailsRecord(csv); | ||
currentNMI = ddr.NMI; | ||
_nmiIntervalLengths[currentNMI] = ddr.IntervalLength; | ||
yield return ddr; | ||
break; | ||
case "300": | ||
if (!headerFound) | ||
throw new InvalidDataException("Data record found before header"); | ||
var idr = ParseIntervalDataRecord(csv, currentNMI); | ||
yield return idr; | ||
break; | ||
case "400": | ||
if (!headerFound) | ||
throw new InvalidDataException("Data record found before header"); | ||
break; | ||
case "500": | ||
if (!headerFound) | ||
throw new InvalidDataException("Data record found before header"); | ||
break; | ||
case "900": | ||
var er = new EndRecord(); | ||
endFound = true; | ||
yield return er; | ||
break; | ||
default: | ||
throw new InvalidDataException($"Unsupported record indicator: {recordIndicator}"); | ||
} | ||
} | ||
if (!headerFound) | ||
throw new InvalidDataException("No header record found"); | ||
if (!endFound) | ||
throw new InvalidDataException("No end record found"); | ||
} | ||
|
||
private HeaderRecord ParseHeaderRecord(CsvDataReader csv) | ||
{ | ||
var dateTimeString = csv.GetString(2); | ||
var dateTime = DateTime.ParseExact(dateTimeString, "yyyyMMddHHmm", CultureInfo.InvariantCulture); | ||
|
||
return new HeaderRecord() | ||
{ | ||
VersionHeader = csv.GetString(1), | ||
DateTime = dateTime, | ||
FromParticipant = csv.GetString(3), | ||
ToParticipant = csv.GetString(4) | ||
}; | ||
} | ||
private NMIDataDetailsRecord ParseNMIDataDetailsRecord(CsvDataReader csv) | ||
{ | ||
var dateString = csv.GetString(9); | ||
var date = DateOnly.ParseExact(dateString, "yyyyMMdd", CultureInfo.InvariantCulture); | ||
|
||
return new NMIDataDetailsRecord | ||
{ | ||
NMI = csv.GetString(1), | ||
NMIConfiguration = csv.GetString(2), | ||
RegisterId = csv.GetString(3), | ||
NMISuffix = csv.GetString(4), | ||
MDMDataStreamIdentifier = csv.GetString(5), | ||
MeterSerialNumber = csv.GetString(6), | ||
UOM = csv.GetString(7), | ||
IntervalLength = csv.GetInt32(8), | ||
NextScheduledReadDate = date | ||
}; | ||
} | ||
|
||
private IntervalDataRecord ParseIntervalDataRecord(CsvDataReader csv, string currentNMI) | ||
{ | ||
var dateString = csv.GetString(1); | ||
var date = DateOnly.ParseExact(dateString, "yyyyMMdd", CultureInfo.InvariantCulture); | ||
int intervalLength = _nmiIntervalLengths[currentNMI]; | ||
int expectedIntervals = 1440 / intervalLength; // 1440 minutes in a day | ||
|
||
return new IntervalDataRecord | ||
{ | ||
IntervalDate = date | ||
// TODO: parse interval values | ||
}; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
using AEMO.MDFF.Abstractions; | ||
|
||
namespace AEMO.MDFF.NEM13; | ||
|
||
public class Nem13Reader : IMdffReader | ||
{ | ||
public IAsyncEnumerable<IMdffRecord> ReadAsync(Stream stream, CancellationToken ct) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
} |