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> 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, @"^(?[-+]?(?:\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); } } }