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
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);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|