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.
156 lines
4.5 KiB
156 lines
4.5 KiB
|
2 weeks ago
|
# DMM Related Code
|
||
|
|
|
||
|
|
This file is a reference-only extraction. It is not compiled by the Housing app.
|
||
|
|
|
||
|
|
## Source Files
|
||
|
|
|
||
|
|
- `Services/EquipmentMeasurementSettings.cs`
|
||
|
|
- `Services/EquipmentMeasurementService.cs`
|
||
|
|
- `Services/IScpiClient.cs`
|
||
|
|
- `Services/TcpScpiClient.cs`
|
||
|
|
- `Services/SerialScpiClient.cs`
|
||
|
|
- `Services/VisaScpiClient.cs`
|
||
|
|
|
||
|
|
## Hardware.ini Keys
|
||
|
|
|
||
|
|
```ini
|
||
|
|
[Equipment]
|
||
|
|
Timeout=5000
|
||
|
|
SettleMilliseconds=300
|
||
|
|
|
||
|
|
; 34465A DMM
|
||
|
|
VoltageConnection=LAN
|
||
|
|
VoltageResource=
|
||
|
|
VoltageIdnMatch=34465A
|
||
|
|
VoltageHost=192.168.10.13
|
||
|
|
VoltagePort=5025
|
||
|
|
VoltageSetupCommand=
|
||
|
|
VoltageReadCommand=MEAS:VOLT:DC?
|
||
|
|
```
|
||
|
|
|
||
|
|
## Settings Load Code
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
Voltage = LoadChannel(
|
||
|
|
values,
|
||
|
|
"Voltage",
|
||
|
|
"MEAS:VOLT:DC?",
|
||
|
|
"34460,34461,34465,34470,3446,3447");
|
||
|
|
```
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
private static ScpiChannelSettings LoadChannel(
|
||
|
|
Dictionary<string, string> values,
|
||
|
|
string prefix,
|
||
|
|
string defaultReadCommand,
|
||
|
|
string defaultIdnMatch)
|
||
|
|
{
|
||
|
|
return new ScpiChannelSettings
|
||
|
|
{
|
||
|
|
Connection = GetString(values, prefix + "Connection", string.Empty),
|
||
|
|
ResourceName = GetString(values, prefix + "Resource", string.Empty),
|
||
|
|
IdnMatch = GetString(values, prefix + "IdnMatch", defaultIdnMatch),
|
||
|
|
Host = GetString(values, prefix + "Host", string.Empty),
|
||
|
|
Port = GetInt(values, prefix + "Port", 5025),
|
||
|
|
PortName = GetString(values, prefix + "PortName", string.Empty),
|
||
|
|
BaudRate = GetInt(values, prefix + "BaudRate", 115200),
|
||
|
|
Terminator = GetString(values, prefix + "Terminator", string.Empty),
|
||
|
|
SetupCommand = GetString(values, prefix + "SetupCommand", string.Empty),
|
||
|
|
CleanupCommand = GetString(values, prefix + "CleanupCommand", string.Empty),
|
||
|
|
ReadCommand = GetString(values, prefix + "ReadCommand", defaultReadCommand)
|
||
|
|
};
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Voltage Read Flow
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
var voltage = await ReadChannelAsync("VOLTAGE", _settings.Voltage, log);
|
||
|
|
```
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
private async Task<decimal> ReadChannelAsync(
|
||
|
|
string label,
|
||
|
|
ScpiChannelSettings channel,
|
||
|
|
StringBuilder log,
|
||
|
|
bool skipSetupCommand = false)
|
||
|
|
{
|
||
|
|
if (IsDisabled(channel))
|
||
|
|
{
|
||
|
|
log.AppendLine($"> {label} SKIP: disabled in Hardware.ini");
|
||
|
|
return 0m;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (string.IsNullOrWhiteSpace(channel.ReadCommand))
|
||
|
|
{
|
||
|
|
throw new InvalidOperationException($"Hardware.ini [Equipment] {label} ReadCommand 값을 확인하세요.");
|
||
|
|
}
|
||
|
|
|
||
|
|
using var client = await CreateClientAsync(label, channel);
|
||
|
|
return await ReadChannelWithClientAsync(label, channel, client, log, skipSetupCommand);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
private static async Task<decimal> ReadChannelWithClientAsync(
|
||
|
|
string label,
|
||
|
|
ScpiChannelSettings channel,
|
||
|
|
IScpiClient client,
|
||
|
|
StringBuilder log,
|
||
|
|
bool skipSetupCommand = false)
|
||
|
|
{
|
||
|
|
if (string.IsNullOrWhiteSpace(channel.ReadCommand))
|
||
|
|
{
|
||
|
|
throw new InvalidOperationException($"Hardware.ini [Equipment] {label} ReadCommand 값을 확인하세요.");
|
||
|
|
}
|
||
|
|
|
||
|
|
log.AppendLine($"> {label} CONNECT: {DescribeClient(client, channel)}");
|
||
|
|
if (!skipSetupCommand && !string.IsNullOrWhiteSpace(channel.SetupCommand))
|
||
|
|
{
|
||
|
|
log.AppendLine($"> {label} SETUP: {channel.SetupCommand}");
|
||
|
|
await SendCommandListAsync(client, channel.SetupCommand);
|
||
|
|
}
|
||
|
|
|
||
|
|
log.AppendLine($"> {label} READ: {channel.ReadCommand}");
|
||
|
|
var response = await client.QueryAsync(channel.ReadCommand);
|
||
|
|
log.AppendLine(response);
|
||
|
|
|
||
|
|
return ExtractFirstDecimal(response, label);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Shared SCPI Connection Code
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
private Task<IScpiClient> CreateClientAsync(string label, ScpiChannelSettings channel)
|
||
|
|
{
|
||
|
|
var connection = GetConnectionType(channel);
|
||
|
|
return connection switch
|
||
|
|
{
|
||
|
|
"TCP" or "LAN" or "ETHERNET" => CreateTcpClientAsync(label, channel),
|
||
|
|
"SERIAL" or "COM" => Task.FromResult<IScpiClient>(
|
||
|
|
new SerialScpiClient(
|
||
|
|
channel.PortName,
|
||
|
|
channel.BaudRate,
|
||
|
|
_settings.Timeout,
|
||
|
|
channel.GetIdnMatches(),
|
||
|
|
channel.GetTerminator())),
|
||
|
|
"VISA" or "USB" => CreateVisaClientAsync(channel),
|
||
|
|
_ => throw new InvalidOperationException($"{label} 장비 연결 방식은 Visa, Serial, Tcp 중 하나로 설정하세요.")
|
||
|
|
};
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
```csharp
|
||
|
|
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);
|
||
|
|
}
|
||
|
|
```
|