2023-07-27 16:50:05 +08:00
|
|
|
|
using Aitex.Core.Common.DeviceData.IoDevice;
|
|
|
|
|
using Aitex.Core.RT.DataCenter;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
using Aitex.Core.RT.Event;
|
|
|
|
|
using Aitex.Core.RT.IOCore;
|
|
|
|
|
using Aitex.Core.RT.Log;
|
|
|
|
|
using Aitex.Core.RT.OperationCenter;
|
|
|
|
|
using Aitex.Core.RT.SCCore;
|
|
|
|
|
using Aitex.Core.Util;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Xml;
|
2023-07-27 16:50:05 +08:00
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
|
|
|
|
namespace Aitex.Core.RT.Device.Devices
|
|
|
|
|
{
|
|
|
|
|
public class IoPSU : BaseDevice, IDevice
|
|
|
|
|
{
|
2023-07-19 14:07:29 +08:00
|
|
|
|
#region Variables
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-07-19 15:18:12 +08:00
|
|
|
|
private float _resLimitMax;
|
|
|
|
|
|
2023-07-19 14:07:29 +08:00
|
|
|
|
private readonly bool _isFloatAioType;
|
|
|
|
|
private readonly AIAccessor _aiOutputVoltage;
|
|
|
|
|
private readonly AIAccessor _aiOutputArms;
|
|
|
|
|
private readonly AIAccessor _aiOutputPower;
|
|
|
|
|
private readonly AIAccessor _aiSimVoltage;
|
|
|
|
|
private readonly AIAccessor _aiSimArms;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
|
|
|
|
//private AOAccessor _aoEnable = null;
|
|
|
|
|
//private AOAccessor _aoReset = null;
|
2023-07-19 14:07:29 +08:00
|
|
|
|
private readonly AOAccessor _aoConstant;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-07-19 14:07:29 +08:00
|
|
|
|
private readonly DIAccessor _diStatus;
|
|
|
|
|
private readonly DIAccessor _diAlarm;
|
|
|
|
|
private readonly DIAccessor _diHeatEnable;
|
|
|
|
|
private DIAccessor _diCommunicationError;
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-07-19 14:07:29 +08:00
|
|
|
|
private readonly DOAccessor _doReset;
|
|
|
|
|
private readonly DOAccessor _doStatus;
|
|
|
|
|
private readonly DOAccessor _doHeatEnable;
|
|
|
|
|
private readonly DOAccessor _doRelatedEnable; //每个Enable同时关联的Inner,Middle,Out Enable
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-07-19 14:07:29 +08:00
|
|
|
|
private readonly R_TRIG _alarmTrig = new();
|
|
|
|
|
private readonly R_TRIG _commAlarmTrig = new();
|
|
|
|
|
private readonly R_TRIG _enableTrig = new();
|
|
|
|
|
private readonly R_TRIG _enableTrig2 = new();
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-07-19 14:07:29 +08:00
|
|
|
|
private readonly R_TRIG _trigVoltage = new();
|
|
|
|
|
private readonly R_TRIG _trigCurrent = new();
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-07-27 16:50:05 +08:00
|
|
|
|
private readonly R_TRIG _trigResOutOfRange = new();
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-07-27 16:50:05 +08:00
|
|
|
|
private readonly DeviceTimer _timResetPulse = new();
|
|
|
|
|
private readonly DeviceTimer _timResOutOfRange = new();
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-07-19 15:18:12 +08:00
|
|
|
|
private readonly IoPsuData _deviceData = new();
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-07-19 14:07:29 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Constructors
|
|
|
|
|
|
2023-07-19 15:18:12 +08:00
|
|
|
|
public IoPSU(string module, XmlElement node, string ioModule = "") : base(module, node, ioModule)
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
|
|
|
|
_aiOutputVoltage = ParseAiNode("aiOutputVoltage", node, ioModule);
|
|
|
|
|
_aiOutputArms = ParseAiNode("aiOutputArms", node, ioModule);
|
|
|
|
|
_aiOutputPower = ParseAiNode("aiOutputPower", node, ioModule);
|
|
|
|
|
_aiSimVoltage = ParseAiNode("aiSimVoltage", node, ioModule);
|
|
|
|
|
_aiSimArms = ParseAiNode("aiSimArms", node, ioModule);
|
|
|
|
|
_aoConstant = ParseAoNode("aoConstant", node, ioModule);
|
|
|
|
|
|
|
|
|
|
_doReset = ParseDoNode("doReset", node, ioModule);
|
|
|
|
|
_doStatus = ParseDoNode("doStatus", node, ioModule);
|
|
|
|
|
_diStatus = ParseDiNode("diStatus", node, ioModule);
|
|
|
|
|
_diAlarm = ParseDiNode("diAlarm", node, ioModule);
|
|
|
|
|
_doHeatEnable = ParseDoNode("doHeatEnable", node, ioModule);
|
|
|
|
|
_diHeatEnable = ParseDiNode("diHeatEnable", node, ioModule);
|
|
|
|
|
_doRelatedEnable = ParseDoNode("doRelatedEnable", node, ioModule);
|
|
|
|
|
_diCommunicationError = ParseDiNode("diCommunicationError", node, ioModule);
|
|
|
|
|
|
|
|
|
|
_isFloatAioType = !string.IsNullOrEmpty(node.GetAttribute("aioType")) && (node.GetAttribute("aioType") == "float");
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-19 14:07:29 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Properties
|
|
|
|
|
|
|
|
|
|
public float OutputVoltageFeedBack => _aiOutputVoltage == null ? 0 : (_isFloatAioType ? _aiOutputVoltage.FloatValue : _aiOutputVoltage.Value);
|
|
|
|
|
|
|
|
|
|
public float OutputArmsFeedBack => _aiOutputArms == null ? 0 : (_isFloatAioType ? _aiOutputArms.FloatValue : _aiOutputArms.Value);
|
|
|
|
|
|
2023-07-19 15:18:12 +08:00
|
|
|
|
public float ResistanceLimitMax => _resLimitMax;
|
|
|
|
|
|
|
|
|
|
public float Resistance => OutputArmsFeedBack == 0 ? 0 : OutputVoltageFeedBack / OutputArmsFeedBack;
|
|
|
|
|
|
|
|
|
|
public bool IsResistanceOutOfRange => IsResistanceTooHigh;
|
|
|
|
|
|
2023-07-27 16:50:05 +08:00
|
|
|
|
public bool IsResistanceTooHigh
|
|
|
|
|
{
|
|
|
|
|
get;
|
|
|
|
|
private set;
|
|
|
|
|
}
|
2023-07-19 14:07:29 +08:00
|
|
|
|
|
|
|
|
|
public float OutputPowerFeedBack => _aiOutputPower == null ? 0 : (_isFloatAioType ? _aiOutputPower.FloatValue : _aiOutputPower.Value);
|
|
|
|
|
|
|
|
|
|
public bool StatusFeedBack => _diStatus?.Value ?? false;
|
|
|
|
|
|
|
|
|
|
public float SimVoltageFeedBack => _aiSimVoltage == null ? 0 : (_isFloatAioType ? _aiSimVoltage.FloatValue : _aiSimVoltage.Value);
|
|
|
|
|
|
|
|
|
|
public float SimArmsFeedBack => _aiSimArms == null ? 0 : (_isFloatAioType ? _aiSimArms.FloatValue : _aiSimArms.Value);
|
|
|
|
|
|
|
|
|
|
public bool AlarmFeedBack => _diAlarm?.Value ?? false;
|
|
|
|
|
|
|
|
|
|
public float ConstantSetPoint
|
|
|
|
|
{
|
|
|
|
|
get => _aoConstant == null ? 0 : (_isFloatAioType ? _aoConstant.FloatValue : _aoConstant.Value);
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (_isFloatAioType)
|
|
|
|
|
{
|
|
|
|
|
_aoConstant.FloatValue = value;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_aoConstant.Value = (short)value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool AllHeatEnable => _diHeatEnable?.Value ?? false;
|
|
|
|
|
|
2023-07-19 15:18:12 +08:00
|
|
|
|
private IoPsuData DeviceData
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
_deviceData.IsAlarm = AlarmFeedBack;
|
2023-07-27 16:50:05 +08:00
|
|
|
|
_deviceData.OutputStatusFeedBack = StatusFeedBack;
|
2023-07-19 15:18:12 +08:00
|
|
|
|
_deviceData.AllHeatEnable = AllHeatEnable;
|
|
|
|
|
_deviceData.ConstantSetPoint = ConstantSetPoint;
|
|
|
|
|
_deviceData.OutputArmsFeedBack = OutputArmsFeedBack;
|
|
|
|
|
_deviceData.OutputPowerFeedBack = OutputPowerFeedBack;
|
|
|
|
|
_deviceData.OutputVoltageFeedback = OutputVoltageFeedBack;
|
|
|
|
|
_deviceData.ResistanceLimitMax = ResistanceLimitMax;
|
|
|
|
|
_deviceData.Resistance = Resistance;
|
|
|
|
|
_deviceData.IsResistanceOutOfRange = IsResistanceOutOfRange;
|
|
|
|
|
_deviceData.IsResistanceTooHigh = IsResistanceTooHigh;
|
|
|
|
|
return _deviceData;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Func<bool, bool> FuncCheckInterLock { get; set; }
|
|
|
|
|
|
2023-07-19 14:07:29 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Methods
|
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
public bool Initialize()
|
|
|
|
|
{
|
|
|
|
|
DATA.Subscribe($"{Module}.{Name}.OutputVoltageFeedBack", () => OutputVoltageFeedBack);
|
|
|
|
|
DATA.Subscribe($"{Module}.{Name}.OutputArmsFeedBack", () => OutputArmsFeedBack);
|
2023-07-19 15:18:12 +08:00
|
|
|
|
DATA.Subscribe($"{Module}.{Name}.ResistanceLimitMax", () => ResistanceLimitMax);
|
2023-04-13 11:51:03 +08:00
|
|
|
|
DATA.Subscribe($"{Module}.{Name}.Resistance", () => Resistance);
|
2023-07-19 15:18:12 +08:00
|
|
|
|
DATA.Subscribe($"{Module}.{Name}.IsResistanceOutOfRange", () => IsResistanceOutOfRange);
|
|
|
|
|
DATA.Subscribe($"{Module}.{Name}.IsResistanceTooHigh", () => IsResistanceTooHigh);
|
2023-04-13 11:51:03 +08:00
|
|
|
|
DATA.Subscribe($"{Module}.{Name}.OutputPowerFeedBack", () => OutputPowerFeedBack);
|
|
|
|
|
DATA.Subscribe($"{Module}.{Name}.StatusFeedBack", () => StatusFeedBack);
|
|
|
|
|
DATA.Subscribe($"{Module}.{Name}.SimVoltageFeedBack", () => SimVoltageFeedBack);
|
|
|
|
|
DATA.Subscribe($"{Module}.{Name}.SimArmsFeedBack", () => SimArmsFeedBack);
|
|
|
|
|
DATA.Subscribe($"{Module}.{Name}.ConstantSetPoint", () => ConstantSetPoint);
|
|
|
|
|
DATA.Subscribe($"{Module}.{Name}.AlarmFeedBack", () => AlarmFeedBack);
|
|
|
|
|
DATA.Subscribe($"{Module}.{Name}.AllHeatEnable", () => AllHeatEnable);
|
2023-07-19 15:18:12 +08:00
|
|
|
|
DATA.Subscribe($"{Module}.{Name}.DeviceData", () => DeviceData);
|
|
|
|
|
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
|
|
|
|
OP.Subscribe($"{Module}.{Name}.SetHeadHeaterEnable", (function, args) =>
|
|
|
|
|
{
|
2023-07-19 14:07:29 +08:00
|
|
|
|
var isTrue = Convert.ToBoolean(args[0]);
|
|
|
|
|
SetHeadHeaterEnable(isTrue, out _);
|
2023-04-13 11:51:03 +08:00
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OP.Subscribe($"{Module}.{Name}.SetPSUEnable", (function, args) =>
|
|
|
|
|
{
|
2023-07-19 14:07:29 +08:00
|
|
|
|
var isTrue = Convert.ToBoolean(args[0]);
|
|
|
|
|
SetPSUEnable(isTrue, out _);
|
2023-04-13 11:51:03 +08:00
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
OP.Subscribe($"{Module}.{Name}.SetPSUReset", (function, args) =>
|
|
|
|
|
{
|
2023-07-19 14:07:29 +08:00
|
|
|
|
var isTrue = Convert.ToBoolean(args[0]);
|
|
|
|
|
SetPSUReset(isTrue, out _);
|
2023-04-13 11:51:03 +08:00
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool SetPSUEnable(bool setValue, out string reason)
|
|
|
|
|
{
|
|
|
|
|
if (!_doStatus.Check(setValue, out reason))
|
|
|
|
|
{
|
|
|
|
|
EV.PostWarningLog(Module, reason);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!_doRelatedEnable.Check(setValue, out reason))
|
|
|
|
|
{
|
|
|
|
|
EV.PostWarningLog(Module, reason);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!_doStatus.SetValue(setValue, out reason))
|
|
|
|
|
{
|
|
|
|
|
EV.PostWarningLog(Module, reason);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!_doRelatedEnable.SetValue(setValue, out reason))
|
|
|
|
|
{
|
|
|
|
|
EV.PostWarningLog(Module, reason);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool SetHeadHeaterEnable(bool setValue, out string reason)
|
|
|
|
|
{
|
|
|
|
|
reason = "";
|
|
|
|
|
if (FuncCheckInterLock != null)
|
|
|
|
|
{
|
|
|
|
|
if (!FuncCheckInterLock(setValue))
|
|
|
|
|
{
|
|
|
|
|
EV.PostInfoLog(Module, $"Set PSU Enable fialed for Interlock!");
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!_doHeatEnable.Check(setValue, out reason))
|
|
|
|
|
{
|
|
|
|
|
EV.PostWarningLog(Module, reason);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!_doHeatEnable.SetValue(setValue, out reason))
|
|
|
|
|
{
|
|
|
|
|
EV.PostWarningLog(Module, reason);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool SetPSUReset(bool setValue, out string reason)
|
|
|
|
|
{
|
|
|
|
|
if (!_doReset.Check(setValue, out reason))
|
|
|
|
|
{
|
|
|
|
|
EV.PostWarningLog(Module, reason);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!_doReset.SetValue(setValue, out reason))
|
|
|
|
|
{
|
|
|
|
|
EV.PostWarningLog(Module, reason);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2023-07-27 16:50:05 +08:00
|
|
|
|
|
|
|
|
|
_timResetPulse.Start(1000);
|
2023-04-13 11:51:03 +08:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public bool CheckPSUEnable()
|
|
|
|
|
{
|
|
|
|
|
return _doStatus.Value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Terminate()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-19 14:07:29 +08:00
|
|
|
|
protected override void HandleMonitor()
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
MonitorEnableTimer();
|
|
|
|
|
MonitorAlarm();
|
2023-07-27 16:50:05 +08:00
|
|
|
|
if (_timResetPulse.IsTimeout())
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
2023-07-27 16:50:05 +08:00
|
|
|
|
_timResetPulse.Stop();
|
2023-04-13 11:51:03 +08:00
|
|
|
|
if (_doReset.Value)
|
|
|
|
|
_doReset.Value = false;
|
|
|
|
|
|
2023-07-27 16:50:05 +08:00
|
|
|
|
ResetResistanceMonitorResult();
|
|
|
|
|
}
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
LOG.Write(ex);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void Reset()
|
|
|
|
|
{
|
|
|
|
|
_alarmTrig.RST = true;
|
|
|
|
|
_commAlarmTrig.RST = true;
|
|
|
|
|
|
|
|
|
|
_trigVoltage.RST = true;
|
|
|
|
|
_trigCurrent.RST = true;
|
2023-07-27 16:50:05 +08:00
|
|
|
|
|
|
|
|
|
ResetResistanceMonitorResult();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 复位加热器电阻监测结果。
|
|
|
|
|
/// </summary>
|
|
|
|
|
private void ResetResistanceMonitorResult()
|
|
|
|
|
{
|
|
|
|
|
IsResistanceTooHigh = false;
|
|
|
|
|
_trigResOutOfRange.RST = true;
|
|
|
|
|
_timResOutOfRange.Stop();
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void MonitorAlarm()
|
|
|
|
|
{
|
|
|
|
|
//检查电阻值是否在合理范围
|
2023-07-27 16:50:05 +08:00
|
|
|
|
_resLimitMax = (float)SC.GetValue<double>($"PM.{Module}.Heater.{Name}ResistanceMax");
|
|
|
|
|
var timeOut = SC.GetValue<int>($"PM.{Module}.Heater.ResistanceMonitorHysteresis");
|
2023-04-13 11:51:03 +08:00
|
|
|
|
|
2023-07-27 16:50:05 +08:00
|
|
|
|
if (Resistance > _resLimitMax && _timResOutOfRange.IsIdle())
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
2023-07-27 16:50:05 +08:00
|
|
|
|
_timResOutOfRange.Start(timeOut * 1000);
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-07-27 16:50:05 +08:00
|
|
|
|
if (Resistance <= _resLimitMax)
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
2023-07-27 16:50:05 +08:00
|
|
|
|
IsResistanceTooHigh = false;
|
|
|
|
|
_timResOutOfRange.Stop();
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
2023-07-27 16:50:05 +08:00
|
|
|
|
_trigResOutOfRange.CLK = _timResOutOfRange.IsTimeout();
|
|
|
|
|
if (_trigResOutOfRange.Q)
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
2023-07-27 16:50:05 +08:00
|
|
|
|
IsResistanceTooHigh = true;
|
|
|
|
|
EV.PostWarningLog(Module, $"{Name} Current resistance {Resistance}ohm exceeds the high limit {_resLimitMax}ohm.");
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void MonitorEnableTimer()
|
|
|
|
|
{
|
2023-07-19 14:07:29 +08:00
|
|
|
|
if (Name == "PSU2")
|
2023-04-13 11:51:03 +08:00
|
|
|
|
{
|
|
|
|
|
_enableTrig.CLK = !_diHeatEnable.Value;
|
|
|
|
|
if (_enableTrig.Q)
|
|
|
|
|
{
|
2023-07-27 16:50:05 +08:00
|
|
|
|
SC.SetItemValue($"PM.{Module}.OpenLidCountDownTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_enableTrig2.CLK = _diHeatEnable.Value;
|
|
|
|
|
if (_enableTrig2.Q)
|
|
|
|
|
{
|
|
|
|
|
SC.SetItemValue($"PM.{Module}.OpenLidCountDownTime", "");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public double AETemp
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
2023-07-27 16:50:05 +08:00
|
|
|
|
var temp = new object();
|
2023-04-13 11:51:03 +08:00
|
|
|
|
if (SC.GetConfigItem("AETemp.EnableDevice").BoolValue)
|
|
|
|
|
{
|
|
|
|
|
temp = DATA.Poll($"{Module}.AETemp.Middle");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (SC.GetConfigItem("AKunTemp.EnableDevice").BoolValue)
|
|
|
|
|
{
|
|
|
|
|
temp = DATA.Poll($"{Module}.AKunTemp.Middle");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return temp == null ? 0 : (double)temp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-19 14:07:29 +08:00
|
|
|
|
#endregion
|
2023-04-13 11:51:03 +08:00
|
|
|
|
}
|
|
|
|
|
}
|