You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

81 lines
3.2 KiB

4 days ago
using System.Globalization;
using System.Text.RegularExpressions;
namespace Housing.Services;
// Adapted from the supplied ScpiMeasurement.cs. Resistance is stored/displayed in kOhm.
public static class ScpiMeasurement
{
public const string ResistanceReadCommand = "MEAS:RES?";
public const decimal OverloadValue = 999m;
public const double OverloadThreshold = 9.0e37;
public static async Task<(decimal Value, bool Overloaded)> ReadResistanceAsync(
Func<string, Task<string>> queryAsync, string? command = null)
{
ArgumentNullException.ThrowIfNull(queryAsync);
var response = await queryAsync(string.IsNullOrWhiteSpace(command)
? ResistanceReadCommand : command).ConfigureAwait(false);
return ParseResponse(response, "RESISTANCE", 0.001m);
}
public static (decimal Value, bool Overloaded) ParseResponse(
string response, string label, decimal scale = 1m)
{
if (string.IsNullOrWhiteSpace(response))
{
throw new InvalidOperationException($"[{label}] 계측기 응답이 비어있습니다 (응답 수신 실패).");
}
var text = response.Trim();
if (text.Equals("OL", StringComparison.OrdinalIgnoreCase) ||
text.Equals("O.L", StringComparison.OrdinalIgnoreCase) ||
text.Equals("OVERLOAD", StringComparison.OrdinalIgnoreCase) ||
text.Equals("OVLD", StringComparison.OrdinalIgnoreCase))
{
return (OverloadValue, true);
}
// Require a complete numeric response; an error message containing a number
// must never be interpreted as a measurement. Only base-unit suffixes are
// accepted so that, for example, mA cannot silently be treated as A.
var unitPattern = label.ToUpperInvariant() switch
{
"VOLTAGE" => @"(?:V|VOLT|VOLTS)",
"CURRENT" => @"(?:A|AMP|AMPS|AMPERE|AMPERES)",
"RESISTANCE" => @"(?:OHM|OHMS|Ω)",
_ => @"(?!)"
};
var match = Regex.Match(text,
@"^(?<number>[-+]?(?:\d+(?:\.\d*)?|\.\d+)(?:[Ee][-+]?\d+)?)(?:\s*" + unitPattern + @")?$",
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
var numericText = match.Groups["number"].Value;
if (!match.Success ||
!double.TryParse(numericText, NumberStyles.Float, CultureInfo.InvariantCulture, out var number) ||
!double.IsFinite(number))
{
throw new InvalidOperationException($"[{label}] 수치 파싱 실패. 원본 응답: [{text}]");
}
// Check overload before converting to decimal or applying the Ohm -> kOhm scale.
if (Math.Abs(number) >= OverloadThreshold)
{
return (OverloadValue, true);
}
if (!decimal.TryParse(numericText, NumberStyles.Float, CultureInfo.InvariantCulture, out var value))
{
throw new InvalidOperationException($"[{label}] 수치 변환 실패. 원본 응답: [{text}]");
}
try
{
return (checked(value * scale), false);
}
catch (OverflowException ex)
{
throw new InvalidOperationException($"[{label}] 단위 변환 실패. 원본 응답: [{text}]", ex);
}
}
}