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.
138 lines
4.5 KiB
138 lines
4.5 KiB
using System.Globalization;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using Housing.Models;
|
|
|
|
namespace Housing.Services;
|
|
|
|
public sealed class EquipmentMeasurementService
|
|
{
|
|
private readonly EquipmentMeasurementSettings _settings;
|
|
|
|
public EquipmentMeasurementService(EquipmentMeasurementSettings settings)
|
|
{
|
|
_settings = settings;
|
|
}
|
|
|
|
public async Task<BoardMeasurementResult> ReadAsync()
|
|
{
|
|
var log = new StringBuilder();
|
|
|
|
if (_settings.SettleMilliseconds > 0)
|
|
{
|
|
await Task.Delay(_settings.SettleMilliseconds);
|
|
}
|
|
|
|
var voltage = await ReadChannelAsync("VOLTAGE", _settings.Voltage, log);
|
|
var current = await ReadChannelAsync("CURRENT", _settings.Current, log);
|
|
|
|
return new BoardMeasurementResult
|
|
{
|
|
Voltage = voltage,
|
|
Current = current,
|
|
RawLog = log.ToString()
|
|
};
|
|
}
|
|
|
|
private async Task<decimal> ReadChannelAsync(string label, ScpiChannelSettings channel, StringBuilder log)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(channel.ReadCommand))
|
|
{
|
|
throw new InvalidOperationException($"Hardware.ini [Equipment] {label} ReadCommand 값을 확인하세요.");
|
|
}
|
|
|
|
using var client = await CreateClientAsync(label, channel);
|
|
log.AppendLine($"> {label} CONNECT: {DescribeClient(client, channel)}");
|
|
|
|
if (!string.IsNullOrWhiteSpace(channel.SetupCommand))
|
|
{
|
|
log.AppendLine($"> {label} SETUP: {channel.SetupCommand}");
|
|
await client.SendAsync(channel.SetupCommand);
|
|
}
|
|
|
|
log.AppendLine($"> {label} READ: {channel.ReadCommand}");
|
|
var response = await client.QueryAsync(channel.ReadCommand);
|
|
log.AppendLine(response);
|
|
|
|
return ExtractFirstDecimal(response, label);
|
|
}
|
|
|
|
private Task<IScpiClient> CreateClientAsync(string label, ScpiChannelSettings channel)
|
|
{
|
|
var connection = GetConnectionType(channel);
|
|
return connection switch
|
|
{
|
|
"TCP" or "LAN" => CreateTcpClientAsync(label, channel),
|
|
"SERIAL" or "COM" => Task.FromResult<IScpiClient>(new SerialScpiClient(channel.PortName, channel.BaudRate, _settings.Timeout, channel.GetIdnMatches())),
|
|
"VISA" or "USB" => CreateVisaClientAsync(channel),
|
|
_ => throw new InvalidOperationException($"{label} 장비 연결 방식은 Visa, Serial, Tcp 중 하나로 설정하세요.")
|
|
};
|
|
}
|
|
|
|
private static string GetConnectionType(ScpiChannelSettings channel)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(channel.Connection))
|
|
{
|
|
return channel.Connection.Trim().ToUpperInvariant();
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(channel.ResourceName))
|
|
{
|
|
return "VISA";
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(channel.IdnMatch))
|
|
{
|
|
return "VISA";
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(channel.PortName))
|
|
{
|
|
return "SERIAL";
|
|
}
|
|
|
|
if (!string.IsNullOrWhiteSpace(channel.Host))
|
|
{
|
|
return "TCP";
|
|
}
|
|
|
|
return string.Empty;
|
|
}
|
|
|
|
private async Task<IScpiClient> CreateTcpClientAsync(string label, ScpiChannelSettings channel)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(channel.Host))
|
|
{
|
|
throw new InvalidOperationException($"Hardware.ini [Equipment] {label} Host 값을 확인하세요.");
|
|
}
|
|
|
|
return await TcpScpiClient.ConnectAsync(channel.Host, channel.Port, _settings.Timeout);
|
|
}
|
|
|
|
private async Task<IScpiClient> CreateVisaClientAsync(ScpiChannelSettings channel)
|
|
{
|
|
return await VisaScpiClient.OpenMatchingAsync(channel.ResourceName, channel.GetIdnMatches(), _settings.Timeout);
|
|
}
|
|
|
|
private static string DescribeClient(IScpiClient client, ScpiChannelSettings channel)
|
|
{
|
|
return client switch
|
|
{
|
|
SerialScpiClient serial => $"Serial {serial.PortName} @ {channel.BaudRate}",
|
|
VisaScpiClient visa => $"VISA {visa.ResourceName}",
|
|
TcpScpiClient => $"TCP {channel.Host}:{channel.Port}",
|
|
_ => channel.Connection
|
|
};
|
|
}
|
|
|
|
private static decimal ExtractFirstDecimal(string response, string label)
|
|
{
|
|
var match = Regex.Match(response, @"[-+]?\d+(?:\.\d+)?(?:[Ee][-+]?\d+)?");
|
|
if (!match.Success)
|
|
{
|
|
throw new InvalidOperationException($"{label} 측정값 파싱 실패");
|
|
}
|
|
|
|
return decimal.Parse(match.Value, NumberStyles.Float, CultureInfo.InvariantCulture);
|
|
}
|
|
}
|
|
|