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.
133 lines
4.0 KiB
133 lines
4.0 KiB
|
2 months ago
|
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();
|
||
|
|
}
|
||
|
|
}
|