设置色彩模式后不重启连接
This commit is contained in:
@@ -466,7 +466,7 @@ namespace JoyD.Windows.CS.Toprie
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 初始化设备管理器
|
||||
/// </summary>
|
||||
@@ -474,16 +474,16 @@ namespace JoyD.Windows.CS.Toprie
|
||||
private bool Initialize()
|
||||
{
|
||||
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 开始执行初始化");
|
||||
|
||||
|
||||
// 在设计模式下,跳过实际初始化,直接返回成功
|
||||
if (IsDesignMode)
|
||||
{
|
||||
{
|
||||
Log("设计模式下跳过实际初始化,模拟初始化成功");
|
||||
_isInitialized = true;
|
||||
_connectionStatus = ConnectionStatus.Connected;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// 双重检查锁定模式,确保线程安全的初始化
|
||||
if (_isInitialized)
|
||||
{
|
||||
@@ -500,111 +500,110 @@ namespace JoyD.Windows.CS.Toprie
|
||||
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 已被其他线程初始化,直接返回成功");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
// 清理旧资源(如果有)
|
||||
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 清理旧资源");
|
||||
CleanupResources();
|
||||
|
||||
// 初始化设备ID列表和相关变量
|
||||
_deviceIds = new List<string>();
|
||||
_currentDeviceId = -1;
|
||||
// 保留默认设备IP,不要重置为空字符串
|
||||
// _deviceIp = string.Empty; // 注释掉这行,避免IP地址被清空
|
||||
_connectionStatus = ConnectionStatus.Disconnected;
|
||||
_currentReconnectAttempt = 0;
|
||||
|
||||
Log("开始SDK初始化...");
|
||||
|
||||
// 首先检查网络可用性
|
||||
if (!IsNetworkAvailable())
|
||||
{
|
||||
Log("网络不可用,初始化暂缓");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 更新状态为连接中
|
||||
UpdateConnectionStatus(ConnectionStatus.Connecting, "正在初始化设备连接");
|
||||
|
||||
// 首先调用SDK静态初始化方法 - 这是连接成功的关键步骤!
|
||||
// 添加重试机制,增强初始化可靠性
|
||||
int initResult = -1;
|
||||
int maxInitRetries = 2;
|
||||
|
||||
for (int retry = 0; retry <= maxInitRetries; retry++)
|
||||
{
|
||||
try
|
||||
// 清理旧资源(如果有)
|
||||
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 清理旧资源");
|
||||
CleanupResources();
|
||||
|
||||
// 初始化设备ID列表和相关变量
|
||||
_deviceIds = new List<string>();
|
||||
_currentDeviceId = -1;
|
||||
// 保留默认设备IP,不要重置为空字符串
|
||||
// _deviceIp = string.Empty; // 注释掉这行,避免IP地址被清空
|
||||
_connectionStatus = ConnectionStatus.Disconnected;
|
||||
_currentReconnectAttempt = 0;
|
||||
|
||||
Log("开始SDK初始化...");
|
||||
|
||||
// 首先检查网络可用性
|
||||
if (!IsNetworkAvailable())
|
||||
{
|
||||
if (retry > 0)
|
||||
{
|
||||
Log($"SDK初始化重试 {retry}/{maxInitRetries}");
|
||||
Thread.Sleep(300); // 重试前等待一小段时间
|
||||
}
|
||||
|
||||
initResult = A8SDK.SDK_initialize();
|
||||
if (initResult == 0)
|
||||
{
|
||||
break; // 成功,跳出循环
|
||||
}
|
||||
|
||||
Log($"SDK静态初始化失败,返回值: {initResult}");
|
||||
Log("网络不可用,初始化暂缓");
|
||||
return false;
|
||||
}
|
||||
catch (Exception initEx)
|
||||
|
||||
// 更新状态为连接中
|
||||
UpdateConnectionStatus(ConnectionStatus.Connecting, "正在初始化设备连接");
|
||||
|
||||
// 首先调用SDK静态初始化方法 - 这是连接成功的关键步骤!
|
||||
// 添加重试机制,增强初始化可靠性
|
||||
int initResult = -1;
|
||||
int maxInitRetries = 2;
|
||||
|
||||
for (int retry = 0; retry <= maxInitRetries; retry++)
|
||||
{
|
||||
Log($"SDK初始化异常: {initEx.Message},堆栈: {initEx.StackTrace}");
|
||||
try
|
||||
{
|
||||
if (retry > 0)
|
||||
{
|
||||
Log($"SDK初始化重试 {retry}/{maxInitRetries}");
|
||||
Thread.Sleep(300); // 重试前等待一小段时间
|
||||
}
|
||||
|
||||
initResult = A8SDK.SDK_initialize();
|
||||
if (initResult == 0)
|
||||
{
|
||||
break; // 成功,跳出循环
|
||||
}
|
||||
|
||||
Log($"SDK静态初始化失败,返回值: {initResult}");
|
||||
}
|
||||
catch (Exception initEx)
|
||||
{
|
||||
Log($"SDK初始化异常: {initEx.Message},堆栈: {initEx.StackTrace}");
|
||||
}
|
||||
}
|
||||
|
||||
if (initResult != 0)
|
||||
{
|
||||
Log($"SDK静态初始化失败,所有重试均未成功,返回值: {initResult}");
|
||||
_isInitialized = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
Log("SDK静态初始化成功");
|
||||
|
||||
// 设置默认配置参数
|
||||
_maxReconnectAttempts = 15; // 增加最大重连次数
|
||||
_isAutoReconnectEnabled = true;
|
||||
|
||||
_isInitialized = true;
|
||||
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 初始化成功,_isInitialized设为true");
|
||||
|
||||
// 更新状态为已连接
|
||||
UpdateConnectionStatus(ConnectionStatus.Connected, "SDK初始化成功");
|
||||
|
||||
// 连接成功后,停止自动重连(如果正在运行)
|
||||
StopAutoReconnect();
|
||||
|
||||
// 启动图像接收
|
||||
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 连接成功,启动图像接收");
|
||||
StartImageReceiving();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (initResult != 0)
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"SDK静态初始化失败,所有重试均未成功,返回值: {initResult}");
|
||||
Log($"SDK初始化失败: {ex.Message},堆栈: {ex.StackTrace}");
|
||||
_isInitialized = false;
|
||||
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 初始化异常,_isInitialized保持false: {ex.Message}");
|
||||
OnConnectionException(new ConnectionExceptionEventArgs(ex, "初始化设备管理器失败"));
|
||||
// 更新状态为断开
|
||||
UpdateConnectionStatus(ConnectionStatus.Disconnected, "初始化失败", ex);
|
||||
return false;
|
||||
}
|
||||
|
||||
Log("SDK静态初始化成功");
|
||||
|
||||
// 设置默认配置参数
|
||||
_maxReconnectAttempts = 15; // 增加最大重连次数
|
||||
_isAutoReconnectEnabled = true;
|
||||
|
||||
_isInitialized = true;
|
||||
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 初始化成功,_isInitialized设为true");
|
||||
|
||||
// 更新状态为已连接
|
||||
UpdateConnectionStatus(ConnectionStatus.Connected, "SDK初始化成功");
|
||||
|
||||
// 连接成功后,停止自动重连(如果正在运行)
|
||||
StopAutoReconnect();
|
||||
|
||||
// 启动图像接收
|
||||
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 连接成功,启动图像接收");
|
||||
StartImageReceiving();
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"SDK初始化失败: {ex.Message},堆栈: {ex.StackTrace}");
|
||||
_isInitialized = false;
|
||||
Log($"[{DateTime.Now:HH:mm:ss.fff}] [线程:{Thread.CurrentThread.ManagedThreadId}] Initialize() - 初始化异常,_isInitialized保持false: {ex.Message}");
|
||||
OnConnectionException(new ConnectionExceptionEventArgs(ex, "初始化设备管理器失败"));
|
||||
// 更新状态为断开
|
||||
UpdateConnectionStatus(ConnectionStatus.Disconnected, "初始化失败", ex);
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 确保在异常情况下也能清理资源
|
||||
if (!_isInitialized && _connectionStatus == ConnectionStatus.Connecting)
|
||||
finally
|
||||
{
|
||||
UpdateConnectionStatus(ConnectionStatus.Disconnected, "初始化未完成");
|
||||
// 确保在异常情况下也能清理资源
|
||||
if (!_isInitialized && _connectionStatus == ConnectionStatus.Connecting)
|
||||
{
|
||||
UpdateConnectionStatus(ConnectionStatus.Disconnected, "初始化未完成");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清理所有资源,确保安全释放
|
||||
/// </summary>
|
||||
@@ -2251,6 +2250,16 @@ namespace JoyD.Windows.CS.Toprie
|
||||
// 即使获取失败,仍尝试设置新值
|
||||
}
|
||||
|
||||
// 将PaletteType枚举转换为int类型
|
||||
int paletteValue = (int)paletteType;
|
||||
|
||||
// 检查新的色彩模式是否与当前值相同,如果相同则不需要设置
|
||||
if (originalValue == paletteValue)
|
||||
{
|
||||
Log($"当前色彩模式已为目标值,无需设置");
|
||||
return true;
|
||||
}
|
||||
|
||||
for (int attempt = 0; attempt < maxRetries; attempt++)
|
||||
{
|
||||
try
|
||||
@@ -2263,9 +2272,7 @@ namespace JoyD.Windows.CS.Toprie
|
||||
continue;
|
||||
}
|
||||
|
||||
// 将PaletteType枚举转换为int类型并发送命令
|
||||
// 按照SDK文档中的参数映射:白热(0)、黑热(1)、铁红(2)、熔岩(3)、彩虹(4)、铁灰(5)、红热(6)、彩虹2(7)
|
||||
int paletteValue = (int)paletteType;
|
||||
// 已在循环外部计算paletteValue,避免重复计算
|
||||
|
||||
// 直接设置色彩模式,不再在每次尝试中读取原始值
|
||||
_a8Sdk.Color_plate = paletteValue;
|
||||
@@ -2276,6 +2283,11 @@ namespace JoyD.Windows.CS.Toprie
|
||||
// 减少读取验证,避免嵌套UDP命令
|
||||
// 信任SDK的设置操作,不再额外验证
|
||||
Log($"色彩模式设置成功: {paletteType} (值: {paletteValue})");
|
||||
|
||||
// 移除图像接收重启逻辑,因为色彩模式不影响图像接收
|
||||
// 仅保留短暂延迟确保设置生效
|
||||
Thread.Sleep(200); // 给设备处理时间
|
||||
|
||||
return true;
|
||||
|
||||
// 注释掉需要再次读取验证的代码,避免嵌套UDP命令
|
||||
|
||||
@@ -22,6 +22,9 @@ namespace JoyD.Windows.CS.Toprie
|
||||
private const string CMD_HEAD = "+CMD";
|
||||
private const int DISCOVERY_UDP_PORT = 18889;
|
||||
private const int SEARCH_DEVICE_MAX_NUM = 99;
|
||||
|
||||
// 日志文件路径
|
||||
private static readonly string LogFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Log.txt");
|
||||
|
||||
// 命令类型枚举
|
||||
private enum CMD_TYPE
|
||||
@@ -460,7 +463,7 @@ namespace JoyD.Windows.CS.Toprie
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
{;
|
||||
try
|
||||
{
|
||||
// 使用SDK格式设置快门校正时间: ip:param_mode,param_value$
|
||||
@@ -483,68 +486,224 @@ namespace JoyD.Windows.CS.Toprie
|
||||
}
|
||||
|
||||
private int _lastKnownColorPlate = 0; // 保存最后已知的色板值
|
||||
private readonly object _colorPlateLock = new object(); // 线程锁,确保日志记录的线程安全
|
||||
|
||||
/// <summary>
|
||||
/// 静态日志写入方法
|
||||
/// </summary>
|
||||
/// <param name="message">日志消息</param>
|
||||
public static void Log(string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 确保日志目录存在
|
||||
string logDirectory = Path.GetDirectoryName(LogFilePath);
|
||||
if (!Directory.Exists(logDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(logDirectory);
|
||||
}
|
||||
|
||||
// 写入日志,不添加额外时间戳(因为原始消息已包含时间戳)
|
||||
File.AppendAllText(LogFilePath, message + Environment.NewLine);
|
||||
|
||||
// 同时输出到控制台
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 如果日志写入失败,至少输出到控制台
|
||||
Console.WriteLine($"[日志写入失败] {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public int Color_plate
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
lock (_colorPlateLock)
|
||||
{
|
||||
// 使用SDK格式获取色板: ip:param_mode,param_value$
|
||||
string command = $"{deviceIp}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.COLOR_PLATE}$";
|
||||
|
||||
if (SendCommand(command, out string response))
|
||||
try
|
||||
{
|
||||
int parsedValue = ParseResponseValue(response);
|
||||
if (parsedValue != 0 || response != null) // 确保至少解析成功或有响应
|
||||
const int maxRetries = 3;
|
||||
int retryCount = 0;
|
||||
bool success = false;
|
||||
int resultValue = _lastKnownColorPlate;
|
||||
|
||||
while (retryCount < maxRetries && !success)
|
||||
{
|
||||
_lastKnownColorPlate = parsedValue;
|
||||
return parsedValue;
|
||||
retryCount++;
|
||||
// 使用SDK格式获取色板: ip:param_mode,param_value$
|
||||
string command = $"{deviceIp}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.COLOR_PLATE}$";
|
||||
|
||||
Log($"[色彩模式读取] 开始获取色彩模式值{(retryCount > 1 ? " (重试 " + retryCount + "/" + maxRetries + ")" : "")},发送命令: {command}");
|
||||
|
||||
if (SendCommand(command, out string response))
|
||||
{
|
||||
Log($"[色彩模式读取] 收到响应: {response}");
|
||||
try
|
||||
{
|
||||
int parsedValue = ParseResponseValue(response);
|
||||
|
||||
if (response != null && parsedValue != -1) // 确保响应有效且解析成功
|
||||
{
|
||||
Log($"[色彩模式读取] 解析成功,当前值: {parsedValue},上一次值: {_lastKnownColorPlate}");
|
||||
_lastKnownColorPlate = parsedValue;
|
||||
resultValue = parsedValue;
|
||||
success = true;
|
||||
}
|
||||
else if (retryCount < maxRetries)
|
||||
{
|
||||
Log($"[色彩模式读取] 解析失败或响应无效,将重试...");
|
||||
System.Threading.Thread.Sleep(200); // 短暂延迟后重试
|
||||
}
|
||||
else
|
||||
{
|
||||
Log($"[色彩模式读取] 解析失败或响应无效,返回上次已知值: {_lastKnownColorPlate}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (retryCount < maxRetries)
|
||||
{
|
||||
Log($"[色彩模式读取] 解析异常: {ex.Message},将重试...");
|
||||
System.Threading.Thread.Sleep(200); // 短暂延迟后重试
|
||||
}
|
||||
else
|
||||
{
|
||||
Log($"[色彩模式读取] 解析异常: {ex.Message},返回上次已知值: {_lastKnownColorPlate}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (retryCount < maxRetries)
|
||||
{
|
||||
Log($"[色彩模式读取] 命令发送失败或超时,将重试...");
|
||||
System.Threading.Thread.Sleep(200); // 短暂延迟后重试
|
||||
}
|
||||
else
|
||||
{
|
||||
Log($"[色彩模式读取] 命令发送失败或超时,返回上次已知值: {_lastKnownColorPlate}");
|
||||
}
|
||||
}
|
||||
Log($"成功读取当前色彩模式值: {resultValue}"); // 添加一致的成功日志
|
||||
|
||||
return resultValue;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[色彩模式读取异常] {ex.Message},返回上次已知值: {_lastKnownColorPlate}");
|
||||
return _lastKnownColorPlate;
|
||||
}
|
||||
Console.WriteLine("获取色板失败,返回上次已知值");
|
||||
return _lastKnownColorPlate; // 返回上次已知值
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"获取色板异常: {ex.Message},返回上次已知值");
|
||||
return _lastKnownColorPlate;
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
try
|
||||
lock (_colorPlateLock)
|
||||
{
|
||||
// 使用SDK格式设置色板: ip:param_mode,param_value$
|
||||
string command = $"{deviceIp}:{(int)CMD_TYPE.SET_COLOR_PLATE},{value}$";
|
||||
|
||||
bool success = SendCommand(command, out string response);
|
||||
if (success && response != null)
|
||||
try
|
||||
{
|
||||
// 验证响应是否包含成功标记
|
||||
bool responseValid = response.Contains("+RET:") &&
|
||||
(!response.Contains("error") || !response.Contains("失败"));
|
||||
int currentValue = GetColor_plateWithoutLock();
|
||||
Log($"[色彩模式设置] 开始设置色彩模式值为: {value},当前值: {currentValue}");
|
||||
|
||||
if (responseValid)
|
||||
// 如果值相同,不需要设置
|
||||
if (currentValue == value)
|
||||
{
|
||||
Console.WriteLine($"设置色板成功: {value}");
|
||||
_lastKnownColorPlate = value; // 更新最后已知值
|
||||
Log($"[色彩模式设置] 当前色彩模式已为目标值,无需设置");
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用SDK格式设置色板: ip:param_mode,param_value$
|
||||
string command = $"{deviceIp}:{(int)CMD_TYPE.SET_COLOR_PLATE},{value}$";
|
||||
|
||||
Log($"[色彩模式设置] 发送命令: {command}");
|
||||
|
||||
bool success = SendCommand(command, out string response);
|
||||
if (success && response != null)
|
||||
{
|
||||
Log($"[色彩模式设置] 收到响应: {response}");
|
||||
|
||||
// 验证响应是否包含成功标记
|
||||
bool responseValid = response.Contains("+RET:") &&
|
||||
(!response.Contains("error") && !response.Contains("失败"));
|
||||
|
||||
if (responseValid)
|
||||
{
|
||||
Log($"[色彩模式设置成功] 从 {_lastKnownColorPlate} 变更为 {value}");
|
||||
_lastKnownColorPlate = value; // 更新最后已知值
|
||||
|
||||
// 验证设置是否生效
|
||||
Log($"[色彩模式设置] 验证设置是否生效...");
|
||||
int validatedValue = GetColor_plateWithoutLock();
|
||||
if (validatedValue == value)
|
||||
{
|
||||
Log($"[色彩模式设置] 验证成功,当前值确认为: {validatedValue}");
|
||||
|
||||
// 设置成功后重启图像接收以避免卡顿
|
||||
Log($"[色彩模式设置] 设置成功后重启图像接收以避免卡顿");
|
||||
SafeRestartImageReceiving();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log($"[色彩模式设置] 验证失败,实际值: {validatedValue},期望值: {value}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log($"[色彩模式设置失败] 响应验证失败: {response}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"设置色板响应验证失败: {response}");
|
||||
Log($"[色彩模式设置失败] 命令发送失败或超时,响应为null: {success}");
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"设置色板失败: 命令发送失败或超时");
|
||||
Log($"[色彩模式设置异常] {ex.Message}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 不带锁获取色彩模式,避免在设置后验证时出现死锁
|
||||
/// </summary>
|
||||
private int GetColor_plateWithoutLock()
|
||||
{
|
||||
try
|
||||
{
|
||||
string command = $"{deviceIp}:{(int)CMD_TYPE.GET_PARAMETER},{(int)PARAM_TYPE.COLOR_PLATE}$";
|
||||
if (SendCommand(command, out string response))
|
||||
{
|
||||
Console.WriteLine($"设置色板异常: {ex.Message}");
|
||||
return ParseResponseValue(response);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"[GetColor_plateWithoutLock异常] {ex.Message}");
|
||||
}
|
||||
return _lastKnownColorPlate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 安全地重启图像接收,避免重复停止和启动
|
||||
/// </summary>
|
||||
private void SafeRestartImageReceiving()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 注意:V8类不直接管理图像接收和连接检查
|
||||
// 这个方法被Color_plate属性调用,主要是为了在设置色彩模式后提供稳定的过渡时间
|
||||
Log("执行色彩模式变更后的稳定处理");
|
||||
|
||||
// 添加短暂延迟以确保设备有时间处理色彩模式变更
|
||||
System.Threading.Thread.Sleep(300);
|
||||
|
||||
Log("色彩模式变更后的稳定处理完成");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"色彩模式变更后稳定处理过程中发生异常: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
public int Mirror_mode
|
||||
@@ -2146,6 +2305,6 @@ namespace JoyD.Windows.CS.Toprie
|
||||
Console.WriteLine($"获取图像数据失败: {ex.Message}");
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user