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.
 
 
 

132 lines
4.0 KiB

using System.IO.Ports;
using System.Text;
using System.Text.RegularExpressions;
namespace Housing.Services;
public sealed class SerialScpiClient : IScpiClient
{
private readonly SerialPort _serialPort;
public string PortName => _serialPort.PortName;
public SerialScpiClient(string portName, int baudRate, int timeoutMilliseconds, IEnumerable<string>? idnMatches = null)
{
var resolvedPortName = ResolvePortName(portName, baudRate, timeoutMilliseconds, idnMatches);
_serialPort = CreateSerialPort(resolvedPortName, baudRate, timeoutMilliseconds);
_serialPort.Open();
_serialPort.DiscardInBuffer();
_serialPort.DiscardOutBuffer();
}
public Task SendAsync(string command)
{
return Task.Run(() => _serialPort.Write(command.Trim() + "\n"));
}
public Task<string> QueryAsync(string command)
{
return Task.Run(() =>
{
_serialPort.DiscardInBuffer();
_serialPort.Write(command.Trim() + "\n");
return _serialPort.ReadLine().Trim();
});
}
private static SerialPort CreateSerialPort(string portName, int baudRate, int timeoutMilliseconds)
{
return new SerialPort(portName, baudRate)
{
Encoding = Encoding.ASCII,
NewLine = "\n",
ReadTimeout = timeoutMilliseconds,
WriteTimeout = timeoutMilliseconds,
DtrEnable = true,
RtsEnable = true
};
}
private static string ResolvePortName(string portName, int baudRate, int timeoutMilliseconds, IEnumerable<string>? idnMatches)
{
if (!IsAutoPort(portName))
{
return portName.Trim();
}
var portNames = SerialPort.GetPortNames()
.OrderBy(GetPortNumber)
.ThenBy(candidate => candidate, StringComparer.OrdinalIgnoreCase)
.ToArray();
if (portNames.Length == 0)
{
throw new InvalidOperationException("사용 가능한 장비 COM 포트를 찾을 수 없습니다.");
}
var tokens = NormalizeIdnMatches(idnMatches);
if (tokens.Length == 0 && portNames.Length == 1)
{
return portNames[0];
}
foreach (var candidate in portNames)
{
if (MatchesIdn(candidate, baudRate, timeoutMilliseconds, tokens))
{
return candidate;
}
}
throw new InvalidOperationException($"SCPI 장비 자동 포트 검색 실패. 확인한 포트: {string.Join(", ", portNames)}");
}
private static bool IsAutoPort(string portName)
{
return string.IsNullOrWhiteSpace(portName) ||
string.Equals(portName.Trim(), "Auto", StringComparison.OrdinalIgnoreCase);
}
private static int GetPortNumber(string portName)
{
var match = Regex.Match(portName, @"\d+");
return match.Success && int.TryParse(match.Value, out var number)
? number
: int.MaxValue;
}
private static string[] NormalizeIdnMatches(IEnumerable<string>? idnMatches)
{
return idnMatches?
.SelectMany(value => value.Split(',', ';'))
.Select(value => value.Trim())
.Where(value => !string.IsNullOrWhiteSpace(value))
.ToArray() ?? Array.Empty<string>();
}
private static bool MatchesIdn(string portName, int baudRate, int timeoutMilliseconds, string[] idnMatches)
{
try
{
using var probe = CreateSerialPort(portName, baudRate, timeoutMilliseconds);
probe.Open();
probe.DiscardInBuffer();
probe.DiscardOutBuffer();
probe.Write("*IDN?\n");
var idn = probe.ReadLine().Trim();
return idnMatches.Length == 0 ||
idnMatches.Any(token => idn.Contains(token, StringComparison.OrdinalIgnoreCase));
}
catch
{
return false;
}
}
public void Dispose()
{
_serialPort.Dispose();
}
}