495 lines
20 KiB
C#
495 lines
20 KiB
C#
using Common.Attributes;
|
||
using DeviceCommand.Base;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Text;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace DeviceCommand.Device
|
||
{
|
||
[BOBCommand]
|
||
public class EAEL9080:ModbusTcp
|
||
{
|
||
public EAEL9080(string IpAddress, int port, int SendTimeout, int ReceiveTimeout)
|
||
{
|
||
ConfigureDevice(IpAddress, port, SendTimeout, ReceiveTimeout);
|
||
ConnectAsync();
|
||
}
|
||
public EAEL9080()
|
||
{
|
||
ConnectAsync();
|
||
}
|
||
#region 一、基础控制与远程模式寄存器
|
||
public async Task SetRemoteControlAsync(bool activate, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort value = (ushort)(activate ? 0xFF00 : 0x0000);
|
||
await WriteSingleRegisterAsync(slaveAddress, 402, value, ct);
|
||
}
|
||
|
||
public async Task SetOutputAsync(bool on, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort value = (ushort)(on ? 0xFF00 : 0x0000);
|
||
await WriteSingleRegisterAsync(slaveAddress, 405, value, ct);
|
||
}
|
||
|
||
public async Task SetWorkModeAsync(bool useResistanceMode, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort value = (ushort)(useResistanceMode ? 0xFF00 : 0x0000);
|
||
await WriteSingleRegisterAsync(slaveAddress, 409, value, ct);
|
||
}
|
||
|
||
public async Task ResetAlarmAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
await WriteSingleRegisterAsync(slaveAddress, 411, 0xFF00, ct);
|
||
}
|
||
|
||
public async Task WriteUserTextAsync(string text, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
if (string.IsNullOrEmpty(text)) return;
|
||
|
||
text = text.Length > 40 ? text[..40] : text;
|
||
int frameCount = (int)Math.Ceiling(text.Length / 4.0);
|
||
|
||
for (int i = 0; i < frameCount; i++)
|
||
{
|
||
string frame = text.Substring(i * 4, Math.Min(4, text.Length - i * 4));
|
||
ushort[] values = new ushort[frame.Length];
|
||
for (int j = 0; j < frame.Length; j++)
|
||
{
|
||
values[j] = frame[j];
|
||
}
|
||
await WriteMultipleRegistersAsync(slaveAddress, (ushort)(171 + i), values, ct);
|
||
}
|
||
}
|
||
public async Task<bool> QueryRemoteControlAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] result = await ReadHoldingRegistersAsync(slaveAddress, 402, 1, ct);
|
||
return result[0] == 0xFF00;
|
||
}
|
||
|
||
public async Task<bool> QueryOutputAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] result = await ReadHoldingRegistersAsync(slaveAddress, 405, 1, ct);
|
||
return result[0] == 0xFF00;
|
||
}
|
||
|
||
public async Task<bool> QueryWorkModeAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] result = await ReadHoldingRegistersAsync(slaveAddress, 409, 1, ct);
|
||
return result[0] == 0xFF00; // true 表示 UIR(电阻模式),false 表示 UIP
|
||
}
|
||
|
||
public async Task<ushort[]> ReadUserTextAsync(byte slaveAddress = 1, int length = 40, CancellationToken ct = default)
|
||
{
|
||
int frameCount = (int)Math.Ceiling(length / 4.0);
|
||
ushort[] textValues = new ushort[length];
|
||
|
||
for (int i = 0; i < frameCount; i++)
|
||
{
|
||
ushort[] frame = await ReadHoldingRegistersAsync(slaveAddress, (ushort)(171 + i), 4, ct);
|
||
for (int j = 0; j < frame.Length && (i * 4 + j) < length; j++)
|
||
{
|
||
textValues[i * 4 + j] = frame[j];
|
||
}
|
||
}
|
||
|
||
return textValues;
|
||
}
|
||
#endregion
|
||
|
||
#region 二、电压 / 电流 / 功率 / 电阻设定值寄存器
|
||
|
||
public async Task SetVoltageAsync(double voltage, double ratedVoltage, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort value = (ushort)(voltage / ratedVoltage * 0xCCCC);
|
||
await WriteSingleRegisterAsync(slaveAddress, 500, value, ct);
|
||
}
|
||
|
||
public async Task<double> GetVoltageAsync(double ratedVoltage, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 500, 1, ct);
|
||
return reg[0] / (double)0xCCCC * ratedVoltage;
|
||
}
|
||
|
||
public async Task SetCurrentAsync(double current, double ratedCurrent, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort value = (ushort)(current / ratedCurrent * 0xCCCC);
|
||
await WriteSingleRegisterAsync(slaveAddress, 501, value, ct);
|
||
}
|
||
|
||
public async Task<double> GetCurrentAsync(double ratedCurrent, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 501, 1, ct);
|
||
return reg[0] / (double)0xCCCC * ratedCurrent;
|
||
}
|
||
|
||
public async Task SetPowerAsync(double power, double ratedPower, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort value = (ushort)(power / ratedPower * 0xCCCC);
|
||
await WriteSingleRegisterAsync(slaveAddress, 502, value, ct);
|
||
}
|
||
|
||
public async Task<double> GetPowerAsync(double ratedPower, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 502, 1, ct);
|
||
return reg[0] / (double)0xCCCC * ratedPower;
|
||
}
|
||
|
||
public async Task SetResistanceAsync(double resistance, double maxResistance, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort value = (ushort)(resistance / maxResistance * 0xCCCC);
|
||
await WriteSingleRegisterAsync(slaveAddress, 503, value, ct);
|
||
}
|
||
|
||
public async Task<double> GetResistanceAsync(double maxResistance, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 503, 1, ct);
|
||
return reg[0] / (double)0xCCCC * maxResistance;
|
||
}
|
||
|
||
// sink 模式示例写方法
|
||
public async Task SetSinkPowerAsync(double power, double sinkRatedPower, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort value = (ushort)(power / sinkRatedPower * 0xCCCC);
|
||
await WriteSingleRegisterAsync(slaveAddress, 498, value, ct);
|
||
}
|
||
|
||
public async Task SetSinkCurrentAsync(double current, double sinkRatedCurrent, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort value = (ushort)(current / sinkRatedCurrent * 0xCCCC);
|
||
await WriteSingleRegisterAsync(slaveAddress, 499, value, ct);
|
||
}
|
||
|
||
public async Task SetSinkResistanceAsync(double resistance, double sinkMaxResistance, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort value = (ushort)(resistance / sinkMaxResistance * 0xCCCC);
|
||
await WriteSingleRegisterAsync(slaveAddress, 504, value, ct);
|
||
}
|
||
|
||
// 读方法
|
||
public async Task<double> GetSinkPowerAsync(double sinkRatedPower, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 498, 1, ct);
|
||
return reg[0] / (double)0xCCCC * sinkRatedPower;
|
||
}
|
||
|
||
public async Task<double> GetSinkCurrentAsync(double sinkRatedCurrent, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 499, 1, ct);
|
||
return reg[0] / (double)0xCCCC * sinkRatedCurrent;
|
||
}
|
||
|
||
public async Task<double> GetSinkResistanceAsync(double sinkMaxResistance, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 504, 1, ct);
|
||
return reg[0] / (double)0xCCCC * sinkMaxResistance;
|
||
}
|
||
|
||
|
||
#endregion
|
||
|
||
#region 三、设备状态与实际值查询寄存器
|
||
|
||
/// <summary>
|
||
/// 读取设备状态寄存器(32 位)
|
||
/// Bit 7:DC 输出/输入开启
|
||
/// Bit 10:远程控制激活
|
||
/// Bit 12:sink 模式
|
||
/// </summary>
|
||
public async Task<uint> ReadDeviceStatusAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 505, 2, ct); // 32位寄存器 = 2 x 16位
|
||
return (uint)(reg[0] << 16 | reg[1]);
|
||
}
|
||
|
||
public async Task<bool> IsDcOnAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
uint status = await ReadDeviceStatusAsync(slaveAddress, ct);
|
||
return (status & (1 << 7)) != 0;
|
||
}
|
||
|
||
public async Task<bool> IsRemoteActiveAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
uint status = await ReadDeviceStatusAsync(slaveAddress, ct);
|
||
return (status & (1 << 10)) != 0;
|
||
}
|
||
|
||
public async Task<bool> IsSinkModeActiveAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
uint status = await ReadDeviceStatusAsync(slaveAddress, ct);
|
||
return (status & (1 << 12)) != 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取实际电压值(单位 V)
|
||
/// 公式: 实际值 = 寄存器值 / 0xCCCC * 额定电压
|
||
/// </summary>
|
||
public async Task<double> ReadActualVoltageAsync(double ratedVoltage, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 507, 1, ct);
|
||
return reg[0] / (double)0xCCCC * ratedVoltage;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取实际电流值(单位 A)
|
||
/// sink 模式下可能为负值,需要根据 Bit 12 判断
|
||
/// </summary>
|
||
public async Task<double> ReadActualCurrentAsync(double ratedCurrent, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 508, 1, ct);
|
||
double current = reg[0] / (double)0xCCCC * ratedCurrent;
|
||
|
||
if (await IsSinkModeActiveAsync(slaveAddress, ct))
|
||
current = -current; // sink 模式电流为负
|
||
|
||
return current;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取实际功率值(单位 W)
|
||
/// sink 模式下可能为负值
|
||
/// </summary>
|
||
public async Task<double> ReadActualPowerAsync(double ratedPower, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 509, 1, ct);
|
||
double power = reg[0] / (double)0xCCCC * ratedPower;
|
||
|
||
if (await IsSinkModeActiveAsync(slaveAddress, ct))
|
||
power = -power;
|
||
|
||
return power;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取额定电压(IEEE 754 float)
|
||
/// </summary>
|
||
public async Task<float> ReadRatedVoltageAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 121, 2, ct);
|
||
byte[] bytes = new byte[4];
|
||
bytes[0] = (byte)(reg[1] & 0xFF);
|
||
bytes[1] = (byte)(reg[1] >> 8);
|
||
bytes[2] = (byte)(reg[0] & 0xFF);
|
||
bytes[3] = (byte)(reg[0] >> 8);
|
||
return BitConverter.ToSingle(bytes, 0);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取额定电流(IEEE 754 float)
|
||
/// </summary>
|
||
public async Task<float> ReadRatedCurrentAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 122, 2, ct);
|
||
byte[] bytes = new byte[4];
|
||
bytes[0] = (byte)(reg[1] & 0xFF);
|
||
bytes[1] = (byte)(reg[1] >> 8);
|
||
bytes[2] = (byte)(reg[0] & 0xFF);
|
||
bytes[3] = (byte)(reg[0] >> 8);
|
||
return BitConverter.ToSingle(bytes, 0);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取额定功率(IEEE 754 float)
|
||
/// </summary>
|
||
public async Task<float> ReadRatedPowerAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 123, 2, ct);
|
||
byte[] bytes = new byte[4];
|
||
bytes[0] = (byte)(reg[1] & 0xFF);
|
||
bytes[1] = (byte)(reg[1] >> 8);
|
||
bytes[2] = (byte)(reg[0] & 0xFF);
|
||
bytes[3] = (byte)(reg[0] >> 8);
|
||
return BitConverter.ToSingle(bytes, 0);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 四、保护与报警相关寄存器
|
||
|
||
/// <summary>
|
||
/// 设置过压保护(OVP)阈值
|
||
/// </summary>
|
||
public async Task SetOverVoltageLimitAsync(double voltage, double ratedVoltage, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort value = (ushort)(voltage / ratedVoltage * 0xCCCC);
|
||
await WriteSingleRegisterAsync(slaveAddress, 510, value, ct);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取过压保护(OVP)阈值
|
||
/// </summary>
|
||
public async Task<double> ReadOverVoltageLimitAsync(double ratedVoltage, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 510, 1, ct);
|
||
return reg[0] / (double)0xCCCC * ratedVoltage;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置过流保护(OCP)阈值
|
||
/// </summary>
|
||
public async Task SetOverCurrentLimitAsync(double current, double ratedCurrent, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort value = (ushort)(current / ratedCurrent * 0xCCCC);
|
||
await WriteSingleRegisterAsync(slaveAddress, 511, value, ct);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取过流保护(OCP)阈值
|
||
/// </summary>
|
||
public async Task<double> ReadOverCurrentLimitAsync(double ratedCurrent, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 511, 1, ct);
|
||
return reg[0] / (double)0xCCCC * ratedCurrent;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置过功率保护(OPP)阈值
|
||
/// </summary>
|
||
public async Task SetOverPowerLimitAsync(double power, double ratedPower, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort value = (ushort)(power / ratedPower * 0xCCCC);
|
||
await WriteSingleRegisterAsync(slaveAddress, 512, value, ct);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取过功率保护(OPP)阈值
|
||
/// </summary>
|
||
public async Task<double> ReadOverPowerLimitAsync(double ratedPower, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 512, 1, ct);
|
||
return reg[0] / (double)0xCCCC * ratedPower;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取 OVP 报警计数器,读取后复位
|
||
/// </summary>
|
||
public async Task<ushort> ReadOVPCountAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 520, 1, ct);
|
||
return reg[0];
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取 OCP 报警计数器
|
||
/// </summary>
|
||
public async Task<ushort> ReadOCPCountAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 521, 1, ct);
|
||
return reg[0];
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取 OPP 报警计数器
|
||
/// </summary>
|
||
public async Task<ushort> ReadOPPCountAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 522, 1, ct);
|
||
return reg[0];
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取过温(OT)报警计数器
|
||
/// </summary>
|
||
public async Task<ushort> ReadOTCountAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 523, 1, ct);
|
||
return reg[0];
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 五、函数发生器与特殊功能寄存器
|
||
|
||
// 寄存器 850:函数发生器启动/停止
|
||
public async Task SetFunctionGeneratorAsync(bool start, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort value = start ? (ushort)0xFF00 : (ushort)0x0000;
|
||
await WriteSingleRegisterAsync(slaveAddress, 850, value, ct);
|
||
}
|
||
|
||
public async Task<bool> ReadFunctionGeneratorAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 850, 1, ct);
|
||
return reg[0] == 0xFF00;
|
||
}
|
||
|
||
// 寄存器 851:函数发生器作用于电压
|
||
public async Task SetFunctionVoltageParamAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
await WriteSingleRegisterAsync(slaveAddress, 851, 0xFF00, ct);
|
||
}
|
||
|
||
public async Task<bool> ReadFunctionVoltageParamAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 851, 1, ct);
|
||
return reg[0] == 0xFF00;
|
||
}
|
||
|
||
// 寄存器 852:函数发生器作用于电流
|
||
public async Task SetFunctionCurrentParamAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
await WriteSingleRegisterAsync(slaveAddress, 852, 0xFF00, ct);
|
||
}
|
||
|
||
public async Task<bool> ReadFunctionCurrentParamAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 852, 1, ct);
|
||
return reg[0] == 0xFF00;
|
||
}
|
||
|
||
// 寄存器 859~861:序列控制
|
||
public async Task SetSequenceStartPointAsync(ushort startPoint, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
if (startPoint < 1 || startPoint > 99) throw new ArgumentOutOfRangeException(nameof(startPoint));
|
||
await WriteSingleRegisterAsync(slaveAddress, 859, startPoint, ct);
|
||
}
|
||
|
||
public async Task<ushort> ReadSequenceStartPointAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 859, 1, ct);
|
||
return reg[0];
|
||
}
|
||
|
||
public async Task SetSequenceEndPointAsync(ushort endPoint, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
if (endPoint < 1 || endPoint > 99) throw new ArgumentOutOfRangeException(nameof(endPoint));
|
||
await WriteSingleRegisterAsync(slaveAddress, 860, endPoint, ct);
|
||
}
|
||
|
||
public async Task<ushort> ReadSequenceEndPointAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 860, 1, ct);
|
||
return reg[0];
|
||
}
|
||
|
||
public async Task SetSequenceLoopCountAsync(ushort loopCount, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
if (loopCount > 999) throw new ArgumentOutOfRangeException(nameof(loopCount));
|
||
await WriteSingleRegisterAsync(slaveAddress, 861, loopCount, ct);
|
||
}
|
||
|
||
public async Task<ushort> ReadSequenceLoopCountAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 861, 1, ct);
|
||
return reg[0];
|
||
}
|
||
|
||
// 寄存器 11000:MPP 跟踪模式
|
||
public async Task SetMPPModeAsync(ushort mode, byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
if (mode != 0x0001 && mode != 0x0002 && mode != 0x0004) throw new ArgumentOutOfRangeException(nameof(mode));
|
||
await WriteSingleRegisterAsync(slaveAddress, 11000, mode, ct);
|
||
}
|
||
|
||
public async Task<ushort> ReadMPPModeAsync(byte slaveAddress = 1, CancellationToken ct = default)
|
||
{
|
||
ushort[] reg = await ReadHoldingRegistersAsync(slaveAddress, 11000, 1, ct);
|
||
return reg[0];
|
||
}
|
||
|
||
#endregion
|
||
|
||
}
|
||
}
|