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.
214 lines
6.9 KiB
214 lines
6.9 KiB
using System.Collections;
|
|
|
|
namespace Housing.Services;
|
|
|
|
public sealed class VisaScpiClient : IScpiClient
|
|
{
|
|
private readonly dynamic _io;
|
|
|
|
private VisaScpiClient(dynamic io, string resourceName)
|
|
{
|
|
_io = io;
|
|
ResourceName = resourceName;
|
|
}
|
|
|
|
public string ResourceName { get; }
|
|
|
|
public static Task<VisaScpiClient> OpenAsync(string resourceName, int timeoutMilliseconds)
|
|
{
|
|
return Task.Run(() => Open(resourceName, timeoutMilliseconds));
|
|
}
|
|
|
|
public static Task<VisaScpiClient> OpenMatchingAsync(
|
|
string resourceName,
|
|
IEnumerable<string> idnMatches,
|
|
int timeoutMilliseconds)
|
|
{
|
|
return string.IsNullOrWhiteSpace(resourceName)
|
|
? Task.Run(() => OpenFirstMatching(idnMatches, timeoutMilliseconds))
|
|
: OpenAsync(resourceName, timeoutMilliseconds);
|
|
}
|
|
|
|
private static VisaScpiClient Open(string resourceName, int timeoutMilliseconds)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(resourceName))
|
|
{
|
|
throw new InvalidOperationException("장비 VISA Resource 값을 확인하세요.");
|
|
}
|
|
|
|
var (resourceManagerType, formattedIoType) = GetVisaComTypes();
|
|
dynamic resourceManager = Activator.CreateInstance(resourceManagerType)!;
|
|
return OpenResource(resourceManager, formattedIoType, resourceName.Trim(), timeoutMilliseconds);
|
|
}
|
|
|
|
private static VisaScpiClient OpenFirstMatching(IEnumerable<string> idnMatches, int timeoutMilliseconds)
|
|
{
|
|
var tokens = NormalizeIdnMatches(idnMatches);
|
|
var (resourceManagerType, formattedIoType) = GetVisaComTypes();
|
|
dynamic resourceManager = Activator.CreateInstance(resourceManagerType)!;
|
|
var resourceNames = FindResourceNames(resourceManager);
|
|
|
|
if (resourceNames.Length == 0)
|
|
{
|
|
throw new InvalidOperationException("VISA 장비를 찾을 수 없습니다. USB/LAN 장비 연결과 VISA 드라이버 설치를 확인하세요.");
|
|
}
|
|
|
|
var checkedResources = new List<string>();
|
|
foreach (var resourceName in resourceNames)
|
|
{
|
|
VisaScpiClient? client = null;
|
|
try
|
|
{
|
|
client = OpenResource(resourceManager, formattedIoType, resourceName, timeoutMilliseconds);
|
|
var idn = client.Query("*IDN?");
|
|
checkedResources.Add($"{resourceName}: {idn}");
|
|
|
|
if (tokens.Length == 0 ||
|
|
tokens.Any(token => idn.Contains(token, StringComparison.OrdinalIgnoreCase)))
|
|
{
|
|
return client;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
checkedResources.Add($"{resourceName}: {ex.Message}");
|
|
}
|
|
|
|
client?.Dispose();
|
|
}
|
|
|
|
var tokenMessage = tokens.Length == 0 ? string.Empty : $" IDN 키워드: {string.Join(", ", tokens)}.";
|
|
var checkedMessage = checkedResources.Count == 0
|
|
? string.Empty
|
|
: $" 확인 결과: {string.Join(" / ", checkedResources)}";
|
|
|
|
throw new InvalidOperationException($"VISA 장비 자동 검색 실패.{tokenMessage}{checkedMessage}");
|
|
}
|
|
|
|
private static (Type ResourceManagerType, Type FormattedIoType) GetVisaComTypes()
|
|
{
|
|
var resourceManagerType = Type.GetTypeFromProgID("VISA.GlobalRM") ??
|
|
Type.GetTypeFromProgID("VisaComLib.ResourceManager");
|
|
var formattedIoType = Type.GetTypeFromProgID("VISA.FormattedIO488") ??
|
|
Type.GetTypeFromProgID("VisaComLib.FormattedIO488");
|
|
|
|
if (resourceManagerType is null || formattedIoType is null)
|
|
{
|
|
throw new InvalidOperationException("VISA 라이브러리를 찾을 수 없습니다. Keysight IO Libraries 또는 NI-VISA 설치가 필요합니다.");
|
|
}
|
|
|
|
return (resourceManagerType, formattedIoType);
|
|
}
|
|
|
|
private static VisaScpiClient OpenResource(
|
|
dynamic resourceManager,
|
|
Type formattedIoType,
|
|
string resourceName,
|
|
int timeoutMilliseconds)
|
|
{
|
|
dynamic session = resourceManager.Open(resourceName);
|
|
|
|
try
|
|
{
|
|
session.Timeout = timeoutMilliseconds;
|
|
}
|
|
catch
|
|
{
|
|
// Some VISA COM sessions expose timeout through driver-specific properties only.
|
|
}
|
|
|
|
dynamic formattedIo = Activator.CreateInstance(formattedIoType)!;
|
|
formattedIo.IO = session;
|
|
return new VisaScpiClient(formattedIo, resourceName);
|
|
}
|
|
|
|
private static string[] FindResourceNames(dynamic resourceManager)
|
|
{
|
|
var resourceNames = new SortedSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
foreach (var pattern in new[] { "?*INSTR", "USB?*INSTR", "TCPIP?*INSTR", "GPIB?*INSTR", "ASRL?*INSTR" })
|
|
{
|
|
try
|
|
{
|
|
AddResourceNames(resourceNames, resourceManager.FindRsrc(pattern));
|
|
}
|
|
catch
|
|
{
|
|
// Resource discovery support differs by VISA COM implementation and backend.
|
|
}
|
|
}
|
|
|
|
return resourceNames.ToArray();
|
|
}
|
|
|
|
private static void AddResourceNames(ISet<string> resourceNames, object? value)
|
|
{
|
|
switch (value)
|
|
{
|
|
case null:
|
|
return;
|
|
case string resourceName:
|
|
AddResourceName(resourceNames, resourceName);
|
|
return;
|
|
case IEnumerable enumerable:
|
|
foreach (var item in enumerable)
|
|
{
|
|
AddResourceName(resourceNames, item?.ToString());
|
|
}
|
|
|
|
return;
|
|
default:
|
|
AddResourceName(resourceNames, value.ToString());
|
|
return;
|
|
}
|
|
}
|
|
|
|
private static void AddResourceName(ISet<string> resourceNames, string? resourceName)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(resourceName))
|
|
{
|
|
resourceNames.Add(resourceName.Trim());
|
|
}
|
|
}
|
|
|
|
private static string[] NormalizeIdnMatches(IEnumerable<string> idnMatches)
|
|
{
|
|
return idnMatches
|
|
.SelectMany(value => value.Split(',', ';'))
|
|
.Select(value => value.Trim())
|
|
.Where(value => !string.IsNullOrWhiteSpace(value))
|
|
.ToArray();
|
|
}
|
|
|
|
public Task SendAsync(string command)
|
|
{
|
|
return Task.Run(() => Send(command));
|
|
}
|
|
|
|
public Task<string> QueryAsync(string command)
|
|
{
|
|
return Task.Run(() => Query(command));
|
|
}
|
|
|
|
private void Send(string command)
|
|
{
|
|
_io.WriteString(command.Trim() + "\n", true);
|
|
}
|
|
|
|
private string Query(string command)
|
|
{
|
|
Send(command);
|
|
return ((string)_io.ReadString()).Trim();
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
try
|
|
{
|
|
_io.IO.Close();
|
|
}
|
|
catch
|
|
{
|
|
// Closing is best-effort because VISA COM implementations differ by vendor.
|
|
}
|
|
}
|
|
}
|
|
|