using HslCommunication;
using HslCommunication.Profinet.OpenProtocol;
using Mapster;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.CodeAnalysis;
using Microsoft.VisualBasic;
using MoYu.Logging;
using Newtonsoft.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using WIDESEAWCS_Common;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_DTO.MOM;
using WIDESEAWCS_DTO.TaskInfo;
using WIDESEAWCS_DTO.WMS;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob;
using WIDESEAWCS_Tasks.ConveyorLineJob;
using static Microsoft.EntityFrameworkCore.DbLoggerCategory.Database;
namespace WIDESEAWCS_Tasks
{
public partial class CommonConveyorLineJob
{
///
/// 处理出库任务
///
private void HandleTaskOut(CommonConveyorLine conveyorLine, ConveyorLineTaskCommand command, string childDeviceCode, int ProtocalDetailValue, Dt_Task taskOut)
{
if (taskOut == null) return;
//ConveyorLineTaskCommand? taskCommand = MapTaskCommand(taskOut, command);
var next = taskOut.NextAddress;
var taskCommand = MapTaskCommand(taskOut, command);
taskOut.NextAddress = next;
bool isOutTray = taskOut.TaskType == (int)TaskOutboundTypeEnum.OutTray;
bool isOutboundAndOutFinish = taskOut.TaskType == (int)TaskOutboundTypeEnum.Outbound && taskOut.TaskState == (int)TaskOutStatusEnum.SC_OutFinish;
bool isOutboundAndLineOutExecuting = taskOut.TaskType == (int)TaskOutboundTypeEnum.Outbound && taskOut.TaskState == (int)TaskOutStatusEnum.Line_OutExecuting;
if (isOutTray || isOutboundAndOutFinish || !isOutboundAndLineOutExecuting)
{
conveyorLine.SendCommand(taskCommand, childDeviceCode);
var log = $"【{conveyorLine._deviceName}】任务号:【{command.TaskNum}】,托盘条码:【{command.Barcode}】已到达【{childDeviceCode}】请求扫码入库,下一目标地址【{taskCommand.TargetAddress}】";
ConsoleHelper.WriteWarningLine(log);
_noticeService.Logs(userTokenIds, new { conveyorLine.DeviceName, log = log, time = DateTime.Now.ToString("G"), color = "red" });
WriteInfo(conveyorLine.DeviceName, log);
ConveyorLineSendFinish(conveyorLine, childDeviceCode, ProtocalDetailValue, true);
_taskService.UpdateTaskStatusToNext(taskOut);
}
else if (taskOut.TaskType == (int)TaskOutboundTypeEnum.OutTray && taskOut.TaskState == (int)TaskOutStatusEnum.Line_OutExecuting)
{
CompleteWmsTask(taskOut, command, conveyorLine, childDeviceCode, ProtocalDetailValue);
}
}
///
/// 处理新任务
///
public async Task HandleNewTaskAsync(CommonConveyorLine conveyorLine, ConveyorLineTaskCommand command, string childDeviceCode, int ProtocalDetailValue)
{
var stationManager = _stationManagerRepository.QueryFirst(x => x.stationChildCode == childDeviceCode && x.stationPLC == conveyorLine.DeviceCode);
switch (stationManager.stationType)
{
case 5:
case 1:
await RequestWmsTask(conveyorLine, command, childDeviceCode, ProtocalDetailValue, stationManager);
break;
case 2:
case 3:
case 4:
case 6:
await CreateAndSendEmptyTrayTask(conveyorLine, command, childDeviceCode, ProtocalDetailValue, stationManager);
break;
case 7:
RequestOutNextAddress(conveyorLine, command, childDeviceCode, ProtocalDetailValue);
break;
case 10:
ConveyorLineOutFinish(conveyorLine, command, childDeviceCode, ProtocalDetailValue);
break;
case 20:
await JZRequestInBound(conveyorLine, command, childDeviceCode, ProtocalDetailValue, stationManager);
break;
default:
break;
}
}
///
/// 映射任务命令
///
private ConveyorLineTaskCommand MapTaskCommand(Dt_Task task, ConveyorLineTaskCommand command)
{
// 使用正则表达式匹配类似 -数字 的模式,并替换为空字符串
task.NextAddress = Regex.Replace(task.NextAddress, @"-(\d+)", "");
if (Convert.ToInt32(task.NextAddress) > 1999)
{
task.NextAddress = (Convert.ToInt32(task.NextAddress) - 1000).ToString();
}
var comm = _mapper.Map(task);
comm.InteractiveSignal = command.InteractiveSignal;
return comm;
}
///
/// 完成WMS任务
///
private void CompleteWmsTask(Dt_Task taskOut, ConveyorLineTaskCommand command, CommonConveyorLine conveyorLine, string childDeviceCode, int ProtocalDetailValue)
{
if (command.Barcode == "NoRead")
{
var NGAddress = _platFormRepository.QueryFirst(x => x.PlatCode == taskOut.TargetAddress).Capacity;
taskOut.TargetAddress = NGAddress.ToString();
}
var keys = new Dictionary()
{
{"taskNum", taskOut.TaskNum}
};
var config = _sys_ConfigService.GetConfigsByCategory(CateGoryConst.CONFIG_SYS_IPAddress);
var wmsBase = config.FirstOrDefault(x => x.ConfigKey == SysConfigKeyConst.WMSIP_BASE)?.ConfigValue;
var completeTask = config.FirstOrDefault(x => x.ConfigKey == SysConfigKeyConst.CompleteTask)?.ConfigValue;
if (wmsBase == null || completeTask == null)
{
throw new InvalidOperationException("WMS IP 未配置");
}
var wmsIpAddress = wmsBase + completeTask;
var result = HttpHelper.GetAsync(wmsIpAddress, keys).Result;
WebResponseContent content = JsonConvert.DeserializeObject(result);
if (content.Status)
{
ConveyorLineSendFinish(conveyorLine, childDeviceCode, ProtocalDetailValue, true);
_taskService.UpdateTaskStatusToNext(taskOut);
}
}
///
/// 创建并发送空托盘任务
///
public Task CreateAndSendEmptyTrayTask(CommonConveyorLine conveyorLine, ConveyorLineTaskCommand command, string childDeviceCode, int ProtocalDetailValue, Dt_StationManager stationManager)
{
if (command.Barcode != "NoRead")
{
string isTrue = string.Empty;
ResultTrayCellsStatus result = GetResultTrayCellsStatus(command, stationManager);
List strings = stationManager.Roadway.Split(",").ToList();
foreach (string item in strings)
{
isTrue = RequestInboundPlatform(item, result.ProductionLine, true);
if (isTrue != null)
{
break;
}
}
WMSTaskDTO taskDTO = null;
if (isTrue != null && isTrue != string.Empty)
{
taskDTO = new WMSTaskDTO
{
TaskNum = _taskRepository.GetTaskNo().Result,
Grade = 1,
PalletCode = command.Barcode,
RoadWay = childDeviceCode,
SourceAddress = childDeviceCode,
TargetAddress = isTrue,
TaskState = (int)TaskOutStatusEnum.SC_OutFinish,
Id = 2,
TaskType = (int)TaskOutboundTypeEnum.OutTray,
ProductionLine = result.ProductionLine,
};
}
else
{
taskDTO = CreateEmptyTrayTaskDto(command.Barcode, childDeviceCode); ;
}
if (_taskRepository.QueryFirst(x => x.PalletCode == taskDTO.PalletCode) != null)
{
WriteInfo(conveyorLine.DeviceName, "当前托盘存在任务");
}
CreateEmptyTryTask(conveyorLine, command, childDeviceCode, ProtocalDetailValue, taskDTO);
}
return Task.CompletedTask;
}
public Task CreateEmptyTryTask(CommonConveyorLine conveyorLine, ConveyorLineTaskCommand command, string childDeviceCode, int ProtocalDetailValue, WMSTaskDTO taskDTO)
{
var content = CreateAndSendTask(taskDTO);
if (content.Status)
{
//var task = _taskService.QueryConveyorLineTask(conveyorLine.DeviceCode, childDeviceCode);
var task = _taskService.QueryConveyorLineTask(conveyorLine.DeviceCode, childDeviceCode, command.Barcode);
if (task != null)
{
var next = task.NextAddress;
var taskCommand = MapTaskCommand(task, command);
task.NextAddress = next;
var log = $"【{conveyorLine._deviceName}】任务号:【{command.TaskNum}】,托盘条码:【{command.Barcode}】已到达【{childDeviceCode}】请求扫码入库(空托盘),下一目标地址【{taskCommand.TargetAddress}】";
ConsoleHelper.WriteWarningLine(log);
_noticeService.Logs(userTokenIds, new { conveyorLine.DeviceName, log = log, time = DateTime.Now.ToString("G"), color = "red" });
WriteInfo(conveyorLine.DeviceName, log);
conveyorLine.SendCommand(taskCommand, childDeviceCode);
ConveyorLineSendFinish(conveyorLine, childDeviceCode, ProtocalDetailValue, true);
_taskService.UpdateTaskStatusToNext(task);
}
}
return Task.CompletedTask;
}
public ResultTrayCellsStatus GetResultTrayCellsStatus(ConveyorLineTaskCommand command, Dt_StationManager stationManager)
{
var config = _sys_ConfigService.GetConfigsByCategory(CateGoryConst.CONFIG_SYS_IPAddress);
var wmsBase = config.FirstOrDefault(x => x.ConfigKey == SysConfigKeyConst.MOMIP_BASE)?.ConfigValue;
var ipAddress = config.FirstOrDefault(x => x.ConfigKey == SysConfigKeyConst.TrayCellsStatus)?.ConfigValue;
if (wmsBase == null || ipAddress == null)
{
throw new InvalidOperationException("MOM IP 未配置");
}
TrayCellsStatusDto trayCells = new TrayCellsStatusDto()
{
Software = "WMS",
TrayBarcode = command.Barcode,
EquipmentCode = stationManager.stationEquipMOM,
SessionId = Guid.NewGuid().ToString(),
EmployeeNo = "MITest",
SceneType = "4",
RequestTime = TimeZoneInfo.ConvertTimeToUtc(DateTime.Now).ToString("yyyy-MM-ddTHH:mm:ss.fffZ")
};
var MOMIpAddress = wmsBase + ipAddress;
var result = HttpHelper.PostAsync(MOMIpAddress, trayCells.ToJsonString()).Result;
WriteInfo("入站校验", $"【{stationManager.stationChildCode}】入站校验请求参数【{trayCells.ToJsonString()}】");
WriteInfo("入站校验", "");
WriteInfo("入站校验", $"【{stationManager.stationChildCode}】入站校验返回参数【{result}】");
ResultTrayCellsStatus result1 = JsonConvert.DeserializeObject(result);
return result1;
}
///
/// 创建空托盘任务DTO
///
private WMSTaskDTO CreateEmptyTrayTaskDto(string barcode, string childDeviceCode)
{
var request = new RequestTaskDto()
{
Position = childDeviceCode,
PalletCode = barcode,
};
var config = _sys_ConfigService.GetConfigsByCategory(CateGoryConst.CONFIG_SYS_IPAddress);
var wmsBase = config.FirstOrDefault(x => x.ConfigKey == SysConfigKeyConst.WMSIP_BASE)?.ConfigValue;
var requestTrayInTask = config.FirstOrDefault(x => x.ConfigKey == SysConfigKeyConst.RequestTrayInTask)?.ConfigValue;
if (wmsBase == null || requestTrayInTask == null)
{
throw new InvalidOperationException("WMS IP 未配置");
}
var wmsIpAddrss = wmsBase + requestTrayInTask;
var result = HttpHelper.PostAsync(wmsIpAddrss, request.ToJsonString()).Result;
if (result == null)
return new WMSTaskDTO();
WebResponseContent content = JsonConvert.DeserializeObject(result);
if (!content.Status)
return new WMSTaskDTO();
return JsonConvert.DeserializeObject(content.Data.ToString());
}
///
/// 请求WMS任务
///
private async Task RequestWmsTask(CommonConveyorLine conveyorLine, ConveyorLineTaskCommand command, string childDeviceCode, int ProtocalDetailValue, Dt_StationManager stationManager)
{
ResultTrayCellsStatus result = GetResultTrayCellsStatus(command, stationManager);
if (childDeviceCode == "1435")
{
ExecuteConveyorLineTask(conveyorLine, command, ProtocalDetailValue, childDeviceCode);
var serialNosError = result.SerialNos.Where(x => x.SerialNoStatus != 1 && x.SerialNoStatus != 4).ToList();
if (serialNosError.Count > 0 || !result.Success || result.SerialNos.Count == 0)
{
WMSTaskDTO taskDTO = new WMSTaskDTO
{
TaskNum = _taskRepository.GetTaskNo().Result,
Grade = 1,
PalletCode = command.Barcode,
RoadWay = "CHSC4",
SourceAddress = childDeviceCode,
TargetAddress = "CHSC4",
TaskState = (int)TaskInStatusEnum.InNew,
Id = 2,
TaskType = (int)TaskInboundTypeEnum.InNG,
ProductionLine = result.ProductionLine,
};
var Taskcontent = _taskService.ReceiveWMSTask(new List { taskDTO });
if (Taskcontent.Status)
{
ExecuteConveyorLineTask(conveyorLine, command, ProtocalDetailValue, childDeviceCode);
}
ConsoleHelper.WriteSuccessLine($"【{stationManager.stationRemark}】【{stationManager.stationChildCode}】{result.MOMMessage}");
return;
}
else
{
var Taskcontent = await _taskService.RequestWMSTask(command.Barcode, childDeviceCode);
if (Taskcontent.Status)
{
ExecuteConveyorLineTask(conveyorLine, command, ProtocalDetailValue, childDeviceCode);
}
}
}
else
{
string isTrue = string.Empty;
ExecuteConveyorLineTask(conveyorLine, command, ProtocalDetailValue, childDeviceCode);
if (result.SerialNos.Count == 0)
{
var Traycontent = await _taskService.RequestWMSTask(command.Barcode, childDeviceCode);
if (Traycontent.Status)
{
ConsoleHelper.WriteSuccessLine("二封空框请求回流");
ExecuteConveyorLineTask(conveyorLine, command, ProtocalDetailValue, childDeviceCode);
}
}
List strings = stationManager.Roadway.Split(",").ToList();
foreach (string item in strings)
{
isTrue = RequestInboundPlatform(item, result.ProductionLine, false);
if (isTrue != null)
{
break;
}
}
if (isTrue != null && isTrue != string.Empty)
{
WMSTaskDTO taskDTO = new WMSTaskDTO
{
TaskNum = _taskRepository.GetTaskNo().Result,
Grade = 1,
PalletCode = command.Barcode,
RoadWay = childDeviceCode,
SourceAddress = childDeviceCode,
TargetAddress = isTrue,
TaskState = (int)TaskOutStatusEnum.SC_OutFinish,
Id = 2,
TaskType = (int)TaskOutboundTypeEnum.OutTray,
ProductionLine = result.ProductionLine,
};
var Taskcontent = _taskService.ReceiveWMSTask(new List { taskDTO });
if (Taskcontent.Status)
{
ExecuteConveyorLineTask(conveyorLine, command, ProtocalDetailValue, childDeviceCode);
}
}
else
{
var Taskcontent = await _taskService.RequestWMSTask(command.Barcode, childDeviceCode);
if (Taskcontent.Status)
{
ExecuteConveyorLineTask(conveyorLine, command, ProtocalDetailValue, childDeviceCode);
}
}
}
}
///
/// 成化入静置
///
///
///
///
///
///
///
///
private async Task JZRequestInBound(CommonConveyorLine conveyorLine, ConveyorLineTaskCommand command, string childDeviceCode, int ProtocalDetailValue, Dt_StationManager stationManager)
{
try
{
StaticVariable.isLineRun = false;
if (StaticVariable.isStackerRun)
{
ExecuteConveyorLineTask(conveyorLine, command, ProtocalDetailValue, childDeviceCode);
}
ResultTrayCellsStatus resultTrayCellsStatus = GetResultTrayCellsStatus(command, stationManager);
//todo判断是否为空框
var serialNosError = resultTrayCellsStatus.SerialNos.Where(x => x.SerialNoStatus != 1 && x.SerialNoStatus != 4).ToList();
if (serialNosError.Count > 0 || !resultTrayCellsStatus.Success)
{
//NG流程
var platform = _platFormRepository.QueryFirst(x => x.ProductionLine == resultTrayCellsStatus.ProductionLine && x.DeviceCode == "1005");
ConveyorLineTaskCommand conveyorLineTaskCommand = new ConveyorLineTaskCommand()
{
TaskNum = 1,
TargetAddress = Convert.ToInt32(platform.Capacity),
Barcode = resultTrayCellsStatus.TrayBarcode,
InteractiveSignal = command.InteractiveSignal
};
conveyorLine.SendCommand(conveyorLineTaskCommand, childDeviceCode);
var logMessage = $"MOM数据异常,送至二封【{resultTrayCellsStatus.ProductionLine}】异常口【{Convert.ToInt32(platform.Capacity)}】";
LogAndSendFinish(conveyorLine, childDeviceCode, ProtocalDetailValue, logMessage, conveyorLineTaskCommand.TargetAddress.ToString());
return;
}
if (resultTrayCellsStatus.SerialNos.Count == 0)
{
var Traycontent = await _taskService.RequestWMSTask(command.Barcode, childDeviceCode);
if (Traycontent.Status)
{
ConsoleHelper.WriteSuccessLine("化成空框请求回流静置");
if (StaticVariable.isStackerRun)
{
ExecuteConveyorLineTask(conveyorLine, command, ProtocalDetailValue, childDeviceCode);
}
return;
}
}
else
{
var configz = _sys_ConfigService.GetConfigsByCategory(CateGoryConst.CONFIG_SYS_IPAddress);
var wmsbase = configz.Where(x => x.ConfigKey == SysConfigKeyConst.WMSIP_BASE).FirstOrDefault()?.ConfigValue;
var address = configz.Where(x => x.ConfigKey == SysConfigKeyConst.QueryStockInfoForRealTrayJZAsync).FirstOrDefault()?.ConfigValue;
if (wmsbase == null || address == null)
{
throw new InvalidOperationException("WMS IP 未配置");
}
var wmsIpAddrss = wmsbase + address;
var result = await HttpHelper.PostAsync(wmsIpAddrss, new { ProductLine = resultTrayCellsStatus.ProductionLine, PalletCode = command.Barcode }.ToJsonString());
var StockInfocontent = JsonConvert.DeserializeObject(result);
if (StockInfocontent.Status)
{
var Taskcontent = await _taskService.RequestWMSTask(command.Barcode, childDeviceCode);
ConsoleHelper.WriteErrorLine($"{JsonConvert.SerializeObject(Taskcontent)}");
if (Taskcontent.Status)
{
if (StaticVariable.isStackerRun)
{
ExecuteConveyorLineTask(conveyorLine, command, ProtocalDetailValue, childDeviceCode);
}
}
else
{
WriteInfo(conveyorLine.DeviceName, Taskcontent.Message);
return;
}
}
else
{
string isTrue = string.Empty;
List strings = stationManager.Roadway.Split(",").ToList();
foreach (string item in strings)
{
isTrue = RequestInboundPlatform(item, resultTrayCellsStatus.ProductionLine, false);
if (isTrue != null)
{
break;
}
}
if (isTrue != null && isTrue != string.Empty)
{
WMSTaskDTO taskDTO = new WMSTaskDTO
{
TaskNum = _taskRepository.GetTaskNo().Result,
Grade = 1,
PalletCode = command.Barcode,
RoadWay = isTrue,
SourceAddress = childDeviceCode,
TargetAddress = isTrue,
TaskState = (int)TaskOutStatusEnum.SC_OutFinish,
Id = 2,
TaskType = (int)TaskOutboundTypeEnum.InToOut,
ProductionLine = resultTrayCellsStatus.ProductionLine,
};
var Taskcontent = _taskService.ReceiveWMSTask(new List { taskDTO });
if (Taskcontent.Status)
{
if (StaticVariable.isStackerRun)
{
ExecuteConveyorLineTask(conveyorLine, command, ProtocalDetailValue, childDeviceCode);
}
return;
}
else
{
WriteInfo(conveyorLine.DeviceName, Taskcontent.Message);
return;
}
}
else
{
var Task = await _taskService.RequestWMSTask(command.Barcode, childDeviceCode);
ConsoleHelper.WriteErrorLine($"{JsonConvert.SerializeObject(Task)}");
if (Task.Status)
{
if (StaticVariable.isStackerRun)
{
ExecuteConveyorLineTask(conveyorLine, command, ProtocalDetailValue, childDeviceCode);
}
}
ConsoleHelper.WriteWarningLine("二封缓存位已满");
return;
}
}
}
}
catch (Exception ex)
{
ConsoleHelper.WriteWarningLine($"{ex.Message}");
}
finally
{
StaticVariable.isLineRun = true;
}
}
///
/// 执行输送线任务
///
/// 输送线对象
/// 输送线任务命令
/// 协议明细值
/// 子设备编号
public void ExecuteConveyorLineTask(CommonConveyorLine conveyorLine, ConveyorLineTaskCommand command, int ProtocalDetailValue, string childDeviceCode)
{
// 查询任务信息
var task = _taskService.QueryBarCodeConveyorLineTask(command.Barcode, childDeviceCode);
if (task == null) return;
// 获取配置值并转换为列表
var value = _sys_ConfigService.GetByConfigKey(CateGoryConst.CONFIG_SYS_InStation, SysConfigKeyConst.JZNGInBoundStation).ConfigValue;
var valueList = value.Split(',').ToList();
// 日志模板
var logMessage = $"【{conveyorLine._deviceName}】任务号:【{task.TaskNum}】,托盘条码:【{task.PalletCode}】已到达【{childDeviceCode}】请求扫码入库(实盘),下一目标地址【{{0}}】";
// 判断任务的起始地址是否在配置列表中
if (valueList.Contains(task.SourceAddress))
{
// 设置目标地址为 "1000"
conveyorLine.SetValue(ConveyorLineDBName.WriteConveyorLineTargetAddress, "1000", childDeviceCode);
// 记录日志并发送完成信号
LogAndSendFinish(conveyorLine, childDeviceCode, ProtocalDetailValue, logMessage, "1000");
}
else if (task.Roadway.Contains("JZ"))
{
// 查询是否存在静置出库任务
var outJZTask = _taskRepository.QueryData(x => x.Roadway == task.Roadway && task.TaskType == (int)TaskOutboundTypeEnum.Outbound &&
(x.TaskState == (int)TaskOutStatusEnum.SC_OutExecuting ||
x.TaskState == (int)TaskOutStatusEnum.SC_OutFinish || x.TaskState == (int)TaskOutStatusEnum.OutNew));
if (!outJZTask.Any())
{
// 映射任务命令
var taskCommand = MapTaskCommand(task, command);
// 发送任务命令
conveyorLine.SendCommand(taskCommand, childDeviceCode);
// 记录日志并发送完成信号
LogAndSendFinish(conveyorLine, childDeviceCode, ProtocalDetailValue, logMessage, taskCommand.TargetAddress.ToString());
// 更新任务状态
_taskService.UpdateTaskStatusToNext(task);
}
else
{
ConsoleHelper.WriteErrorLine("已存在静置出库任务,静置入库任务无法下发至线体");
}
}
else
{
// 映射任务命令
var taskCommand = MapTaskCommand(task, command);
// 发送任务命令
conveyorLine.SendCommand(taskCommand, childDeviceCode);
// 记录日志并发送完成信号
LogAndSendFinish(conveyorLine, childDeviceCode, ProtocalDetailValue, logMessage, taskCommand.TargetAddress.ToString());
// 更新任务状态
_taskService.UpdateTaskStatusToNext(task);
}
}
///
/// 记录日志并发送完成信号
///
/// 输送线对象
/// 子设备编号
/// 协议明细值
/// 日志消息模板
/// 目标地址
private void LogAndSendFinish(CommonConveyorLine conveyorLine, string childDeviceCode, int ProtocalDetailValue, string logMessage, string targetAddress)
{
// 格式化日志消息
var log = string.Format(logMessage, targetAddress);
// 输出警告日志
ConsoleHelper.WriteWarningLine(log);
// 记录日志
_noticeService.Logs(userTokenIds, new { conveyorLine.DeviceName, log, time = DateTime.Now.ToString("G"), color = "red" });
WriteInfo(conveyorLine.DeviceName, log);
// 发送完成信号
ConveyorLineSendFinish(conveyorLine, childDeviceCode, ProtocalDetailValue, true);
}
}
}