Skip to content

Commit

Permalink
Merge pull request #5 from gemstone/comtrade-invariant-culture
Browse files Browse the repository at this point in the history
Use invariant culture for all numeric conversions
  • Loading branch information
ritchiecarroll authored Jun 14, 2024
2 parents def7572 + 5d4475e commit 53f6081
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 60 deletions.
18 changes: 9 additions & 9 deletions src/Gemstone.COMTRADE/AnalogChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,21 +153,21 @@ public AnalogChannel(string lineImage, int version = 1999, bool targetFloatingPo
if (parts.Length < 10 || !useRelaxedValidation && parts.Length != 10 && parts.Length != 13)
throw new InvalidOperationException($"Unexpected number of line image elements for analog channel definition: {parts.Length} - expected 10 or 13{Environment.NewLine}Image = {lineImage}");

Index = int.Parse(parts[0].Trim());
Index = int.Parse(parts[0].Trim(), CultureInfo.InvariantCulture);
Name = parts[1];
Units = parts[4]; // Assign Units before PhaseID
PhaseID = parts[2];
CircuitComponent = parts[3];
Multiplier = double.Parse(parts[5].Trim());
Adder = double.Parse(parts[6].Trim());
Skew = double.Parse(parts[7].Trim());
MinValue = double.Parse(parts[8].Trim());
MaxValue = double.Parse(parts[9].Trim());
Multiplier = double.Parse(parts[5].Trim(), CultureInfo.InvariantCulture);
Adder = double.Parse(parts[6].Trim(), CultureInfo.InvariantCulture);
Skew = double.Parse(parts[7].Trim(), CultureInfo.InvariantCulture);
MinValue = double.Parse(parts[8].Trim(), CultureInfo.InvariantCulture);
MaxValue = double.Parse(parts[9].Trim(), CultureInfo.InvariantCulture);

if (parts.Length >= 13)
{
PrimaryRatio = double.Parse(parts[10].Trim());
SecondaryRatio = double.Parse(parts[11].Trim());
PrimaryRatio = double.Parse(parts[10].Trim(), CultureInfo.InvariantCulture);
SecondaryRatio = double.Parse(parts[11].Trim(), CultureInfo.InvariantCulture);
ScalingIdentifier = parts[12].Trim()[0];
}
}
Expand Down Expand Up @@ -561,7 +561,7 @@ public override string ToString()
// An,ch_id,ph,ccbm,uu,a,b,skew,min,max
List<string> values = new()
{
Index.ToString(),
Index.ToString(CultureInfo.InvariantCulture),
Name ?? string.Empty,
PhaseID,
CircuitComponent ?? string.Empty,
Expand Down
9 changes: 5 additions & 4 deletions src/Gemstone.COMTRADE/DigitalChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

using System;
using System.Collections.Generic;
using System.Globalization;
using Gemstone.StringExtensions;
#if NETSTANDARD
using Newtonsoft.Json;
Expand Down Expand Up @@ -79,15 +80,15 @@ public DigitalChannel(string lineImage, int version = 1999, bool useRelaxedValid

if (parts.Length >= 5)
{
Index = int.Parse(parts[0].Trim());
Index = int.Parse(parts[0].Trim(), CultureInfo.InvariantCulture);
Name = parts[1];
PhaseID = parts[2];
CircuitComponent = parts[3];
NormalState = parts[4].Trim().ParseBoolean();
}
else
{
Index = int.Parse(parts[0].Trim());
Index = int.Parse(parts[0].Trim(), CultureInfo.InvariantCulture);
Name = parts[1];
NormalState = parts[2].Trim().ParseBoolean();
}
Expand Down Expand Up @@ -203,7 +204,7 @@ public override string ToString()
// Dn,ch_id,ph,ccbm,y
values = new List<string>
{
Index.ToString(),
Index.ToString(CultureInfo.InvariantCulture),
Name ?? string.Empty,
PhaseID ?? string.Empty,
CircuitComponent ?? string.Empty,
Expand All @@ -215,7 +216,7 @@ public override string ToString()
// Dn,ch_id,y
values = new List<string>
{
Index.ToString(),
Index.ToString(CultureInfo.InvariantCulture),
Name ?? string.Empty,
NormalState ? "1" : "0"
};
Expand Down
7 changes: 4 additions & 3 deletions src/Gemstone.COMTRADE/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
//******************************************************************************************************

using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
Expand Down Expand Up @@ -402,7 +403,7 @@ private bool ReadNextAscii()
}

// Parse row of data
uint sample = uint.Parse(elems[0]);
uint sample = uint.Parse(elems[0], CultureInfo.InvariantCulture);

// Capture initial sample index - this handles cases where sample index does not start at zero
if (m_initialSample == uint.MaxValue)
Expand All @@ -426,7 +427,7 @@ private bool ReadNextAscii()

// Fall back on specified microsecond time
if (Timestamp == DateTime.MinValue)
Timestamp = new DateTime(Ticks.FromMicroseconds(double.Parse(elems[1]) * m_schema!.TimeFactor) + m_schema.StartTime.Value);
Timestamp = new DateTime(Ticks.FromMicroseconds(double.Parse(elems[1], CultureInfo.InvariantCulture) * m_schema!.TimeFactor) + m_schema.StartTime.Value);

// Apply timestamp offset to restore UTC timezone
if (AdjustToUTC)
Expand All @@ -438,7 +439,7 @@ private bool ReadNextAscii()
// Parse all record values
for (int i = 0; i < Values.Length; i++)
{
Values[i] = double.Parse(elems[i + 2]);
Values[i] = double.Parse(elems[i + 2], CultureInfo.InvariantCulture);

if (i < m_schema!.AnalogChannels?.Length)
Values[i] = AdjustValue(Values[i], i);
Expand Down
14 changes: 11 additions & 3 deletions src/Gemstone.COMTRADE/SampleRate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ public SampleRate(string lineImage, bool useRelaxedValidation = false)
if (parts.Length < 2 || (!useRelaxedValidation && parts.Length != 2))
throw new InvalidOperationException($"Unexpected number of line image elements for sample rate definition: {parts.Length} - expected 2{Environment.NewLine}Image = {lineImage}");

Rate = double.Parse(parts[0].Trim());
EndSample = long.Parse(parts[1].Trim());
Rate = double.Parse(parts[0].Trim(), CultureInfo.InvariantCulture);
EndSample = long.Parse(parts[1].Trim(), CultureInfo.InvariantCulture);
}

#endregion
Expand All @@ -76,7 +76,15 @@ public SampleRate(string lineImage, bool useRelaxedValidation = false)
/// Converts <see cref="SampleRate"/> to its string format.
/// </summary>
public override string ToString() => // samp,endsamp
$"{Rate.ToString(CultureInfo.InvariantCulture)},{EndSample}";
Format($"{Rate},{EndSample}");

#endregion

#region [ Static ]

// Static Methods
private static string Format(FormattableString formattableString) =>
formattableString.ToString(CultureInfo.InvariantCulture);

#endregion
}
Expand Down
40 changes: 20 additions & 20 deletions src/Gemstone.COMTRADE/Schema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ public Schema(string fileName, bool useRelaxedValidation)
DeviceID = parts[1].Trim();

if (parts.Length >= 3 && !string.IsNullOrWhiteSpace(parts[2]))
Version = int.Parse(parts[2].Trim());
Version = int.Parse(parts[2].Trim(), CultureInfo.InvariantCulture);
else
Version = 1991;

Expand All @@ -258,9 +258,9 @@ public Schema(string fileName, bool useRelaxedValidation)
if (parts.Length < 3 || (!useRelaxedValidation && parts.Length != 3))
throw new InvalidOperationException($"Unexpected number of line image elements for second configuration file line: {parts.Length} - expected 3{Environment.NewLine}Image = {lines[lineNumber - 1]}");

int totalChannels = int.Parse(parts[0].Trim());
int totalAnalogChannels = int.Parse(parts[1].Trim().Split('A')[0]);
int totalDigitalChannels = int.Parse(parts[2].Trim().Split('D')[0]);
int totalChannels = int.Parse(parts[0].Trim(), CultureInfo.InvariantCulture);
int totalAnalogChannels = int.Parse(parts[1].Trim().Split('A')[0], CultureInfo.InvariantCulture);
int totalDigitalChannels = int.Parse(parts[2].Trim().Split('D')[0], CultureInfo.InvariantCulture);

if (totalChannels != totalAnalogChannels + totalDigitalChannels)
throw new InvalidOperationException($"Total defined channels must equal the sum of the total number of analog and digital channel definitions.{Environment.NewLine}Image = {lines[lineNumber - 1]}");
Expand All @@ -280,10 +280,10 @@ public Schema(string fileName, bool useRelaxedValidation)
DigitalChannels = digitalChannels.ToArray();

// Parse line frequency
NominalFrequency = double.Parse(lines[lineNumber++]);
NominalFrequency = double.Parse(lines[lineNumber++], CultureInfo.InvariantCulture);

// Parse total number of sample rates
int totalSampleRates = int.Parse(lines[lineNumber++]);
int totalSampleRates = int.Parse(lines[lineNumber++], CultureInfo.InvariantCulture);

if (totalSampleRates == 0)
totalSampleRates = 1;
Expand All @@ -310,7 +310,7 @@ public Schema(string fileName, bool useRelaxedValidation)
AnalogChannels = analogLineImages.Select(lineImage => new AnalogChannel(lineImage, Version, targetFloatingPoint, useRelaxedValidation)).ToArray();

// Parse time factor
TimeFactor = lineNumber < lines.Length ? double.Parse(lines[lineNumber++]) : 1;
TimeFactor = lineNumber < lines.Length ? double.Parse(lines[lineNumber++], CultureInfo.InvariantCulture) : 1;

// Parse time information line
if (lineNumber < lines.Length)
Expand Down Expand Up @@ -339,10 +339,10 @@ public Schema(string fileName, bool useRelaxedValidation)
parts = lines[lineNumber/*++*/].Split(',');

if (parts.Length > 0)
TimeQualityIndicatorCode = (TimeQualityIndicatorCode)byte.Parse(parts[0], NumberStyles.HexNumber);
TimeQualityIndicatorCode = (TimeQualityIndicatorCode)byte.Parse(parts[0], NumberStyles.HexNumber, CultureInfo.InvariantCulture);

if (parts.Length > 1)
LeapSecondIndicator = (LeapSecondIndicator)byte.Parse(parts[1]);
LeapSecondIndicator = (LeapSecondIndicator)byte.Parse(parts[1], CultureInfo.InvariantCulture);
}
}

Expand Down Expand Up @@ -523,11 +523,11 @@ public string FileImage
{
StringBuilder fileImage = new();

void appendLine(string line)
void appendLine(FormattableString line)
{
// The standard .NET "Environment.NewLine" constant can just be a line feed on some operating systems,
// but the COMTRADE standard requires that end of line markers be both a carriage return and line feed.
fileImage.Append(line);
fileImage.Append(line.ToString(CultureInfo.InvariantCulture));
fileImage.Append(Writer.CRLF);
}

Expand All @@ -539,17 +539,17 @@ void appendLine(string line)

// Write analog definitions
for (int i = 0; i < TotalAnalogChannels; i++)
appendLine(AnalogChannels![i].ToString());
appendLine($"{AnalogChannels![i]}");

// Write digital definitions
for (int i = 0; i < TotalDigitalChannels; i++)
appendLine(DigitalChannels![i].ToString());
appendLine($"{DigitalChannels![i]}");

// Write line frequency
appendLine(NominalFrequency.ToString(CultureInfo.InvariantCulture));
appendLine($"{NominalFrequency}");

// Write total number of sample rates (zero signifies no fixed sample rates)
appendLine(TotalSampleRates.ToString());
appendLine($"{TotalSampleRates}");

int totalSampleRates = TotalSampleRates;

Expand All @@ -558,18 +558,18 @@ void appendLine(string line)

// Write sample rates
for (int i = 0; i < totalSampleRates; i++)
appendLine(SampleRates![i].ToString());
appendLine($"{SampleRates![i]}");

// Write timestamps
appendLine(StartTime.ToString());
appendLine(TriggerTime.ToString());
appendLine($"{StartTime}");
appendLine($"{TriggerTime}");

// Write file type
appendLine(FileType.ToString().ToUpper());
appendLine($"{FileType.ToString().ToUpper()}");

// Write time factor
if (Version >= 1999)
appendLine(TimeFactor.ToString(CultureInfo.InvariantCulture));
appendLine($"{TimeFactor}");

// Write per data set time info and state
if (Version >= 2013)
Expand Down
18 changes: 14 additions & 4 deletions src/Gemstone.COMTRADE/TimeOffset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@


using System;
using System.Globalization;

#if NETSTANDARD
using Newtonsoft.Json;
#else
Expand Down Expand Up @@ -76,14 +78,14 @@ public TimeOffset(string lineImage)
switch (parts.Length)
{
case 1:
validFormat = int.TryParse(lineImage, out hours);
validFormat = int.TryParse(lineImage, NumberStyles.Integer, CultureInfo.InvariantCulture, out hours);
break;
case 2:
{
validFormat = int.TryParse(parts[0], out hours);
validFormat = int.TryParse(parts[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out hours);

if (validFormat)
validFormat = int.TryParse(parts[1], out minutes);
validFormat = int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out minutes);
break;
}
default:
Expand Down Expand Up @@ -167,7 +169,15 @@ public long TickOffset
/// Converts <see cref="TimeOffset"/> to its string format.
/// </summary>
public override string ToString() =>
NotApplicable ? "x" : $"{Hours}h{Minutes:00}";
NotApplicable ? "x" : Format($"{Hours}h{Minutes:00}");

#endregion

#region [ Static ]

// Static Methods
private static string Format(FormattableString formattableString) =>
formattableString.ToString(CultureInfo.InvariantCulture);

#endregion
}
Expand Down
15 changes: 13 additions & 2 deletions src/Gemstone.COMTRADE/Timestamp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ public Timestamp(string lineImage)

if (parts.Length == 4)
{
double.TryParse(parts[^1], out milliseconds);
TryParse(parts[^1], out milliseconds);
parts = new[] { parts[0], parts[1], parts[2] };
}

double.TryParse(parts[^1], out double seconds);
TryParse(parts[^1], out double seconds);

seconds += milliseconds;

Expand Down Expand Up @@ -97,5 +97,16 @@ public override string ToString() =>
Value.ToString("dd/MM/yyyy,HH:mm:ss.ffffff", CultureInfo.InvariantCulture);

#endregion

#region [ Static ]

// Static Methods
private static bool TryParse(string s, out double result)
{
NumberStyles style = NumberStyles.Float | NumberStyles.AllowThousands;
return double.TryParse(s, style, CultureInfo.InvariantCulture, out result);
}

#endregion
}
}
Loading

0 comments on commit 53f6081

Please sign in to comment.