|
|
|
|
using Aucma.Scada.Business;
|
|
|
|
|
using Aucma.Scada.Model.domain;
|
|
|
|
|
using Aucma.Scada.Model.dto;
|
|
|
|
|
using Aucma.Scada.UI.Page.AssemblyPlan;
|
|
|
|
|
using Aucma.Scada.UI.Page.InventoryInfo;
|
|
|
|
|
using Aucma.Scada.UI.Page.Model;
|
|
|
|
|
using GalaSoft.MvvmLight;
|
|
|
|
|
using GalaSoft.MvvmLight.Command;
|
|
|
|
|
using HighWayIot.Common;
|
|
|
|
|
using HighWayIot.Config;
|
|
|
|
|
using HighWayIot.Log4net;
|
|
|
|
|
using HighWayIot.Repository;
|
|
|
|
|
using HighWayIot.Repository.service;
|
|
|
|
|
using LiveCharts;
|
|
|
|
|
using LiveCharts.Defaults;
|
|
|
|
|
using LiveCharts.Wpf;
|
|
|
|
|
using MySqlX.XDevAPI.Common;
|
|
|
|
|
using MySqlX.XDevAPI.Relational;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Collections.ObjectModel;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Reflection.Emit;
|
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using System.Windows;
|
|
|
|
|
using System.Windows.Data;
|
|
|
|
|
using System.Windows.Documents;
|
|
|
|
|
using System.Windows.Media;
|
|
|
|
|
|
|
|
|
|
namespace Aucma.Scada.UI.viewModel.AssemblyPlan
|
|
|
|
|
{
|
|
|
|
|
public class BindingPageViewModel : ViewModelBase
|
|
|
|
|
{
|
|
|
|
|
private LogHelper logHelper = LogHelper.Instance;
|
|
|
|
|
|
|
|
|
|
private SpeechStr speechStr = SpeechStr.Instance;
|
|
|
|
|
private AppConfig appConfig = AppConfig.Instance;
|
|
|
|
|
// 箱壳条码
|
|
|
|
|
private static string shellCode = string.Empty;
|
|
|
|
|
|
|
|
|
|
// 内胆码
|
|
|
|
|
private static string linerCode = string.Empty;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private OutStoreBusiness outStoreBusiness = OutStoreBusiness.Instance;
|
|
|
|
|
private IRealTaskInfoService _taskInfoService;
|
|
|
|
|
|
|
|
|
|
private ICodeBindingRecordServices _codeBindingRecordServices;
|
|
|
|
|
private IBaseMaterialInfoService _materialInfoService;
|
|
|
|
|
private RegisterServices registerServices = RegisterServices.Instance;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 刷新下发计划界面图表
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="storeCode"></param>
|
|
|
|
|
/// <param name="taskCode"></param>
|
|
|
|
|
public delegate void RefreshCharts();
|
|
|
|
|
public static event RefreshCharts RefreshChartsEvent;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public BindingPageViewModel()
|
|
|
|
|
{
|
|
|
|
|
_codeBindingRecordServices = registerServices.GetService<ICodeBindingRecordServices>();
|
|
|
|
|
_materialInfoService = registerServices.GetService<IBaseMaterialInfoService>();
|
|
|
|
|
_taskInfoService = registerServices.GetService<IRealTaskInfoService>();
|
|
|
|
|
GunBusiness.RefreshMaterialCodeStrEvent += RefreshMaterialCode;
|
|
|
|
|
LoadData();
|
|
|
|
|
LoadCharts();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// flag箱壳码1,内胆码2
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="str"></param>
|
|
|
|
|
/// <param name="flag"></param>
|
|
|
|
|
public void RefreshMaterialCode(string str, int flag)
|
|
|
|
|
{
|
|
|
|
|
if (flag == 1)
|
|
|
|
|
{
|
|
|
|
|
logHelper.Info($"扫描到箱体码:{str}");
|
|
|
|
|
shellCode = str;
|
|
|
|
|
RefreshCode(str, flag);
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(shellCode) && !string.IsNullOrEmpty(linerCode))
|
|
|
|
|
{
|
|
|
|
|
BindingCode(shellCode, linerCode);
|
|
|
|
|
Clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
logHelper.Info($"扫描到内胆码:{str}");
|
|
|
|
|
linerCode = str;
|
|
|
|
|
|
|
|
|
|
RefreshCode(str, flag);
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(shellCode) && !string.IsNullOrEmpty(linerCode))
|
|
|
|
|
{
|
|
|
|
|
BindingCode(shellCode, linerCode);
|
|
|
|
|
Clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void RefreshCode(string code, int flag)
|
|
|
|
|
{
|
|
|
|
|
if (flag == 1)
|
|
|
|
|
{
|
|
|
|
|
DateTime time = System.DateTime.Now;
|
|
|
|
|
Code1 = code;
|
|
|
|
|
Code1Time = time.ToString();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
DateTime time = System.DateTime.Now;
|
|
|
|
|
Code2 = code;
|
|
|
|
|
Code2Time = time.ToString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 页面扫码信息清空
|
|
|
|
|
/// </summary>
|
|
|
|
|
public void Clear()
|
|
|
|
|
{
|
|
|
|
|
shellCode = string.Empty;
|
|
|
|
|
linerCode = string.Empty;
|
|
|
|
|
// 页面扫码信息清空
|
|
|
|
|
Code1 = string.Empty;
|
|
|
|
|
Code2 = string.Empty;
|
|
|
|
|
Code1Time = string.Empty;
|
|
|
|
|
Code2Time = string.Empty;
|
|
|
|
|
BindingInfo = string.Empty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 提示信息刷新并且存日志
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="logStr"></param>
|
|
|
|
|
private void RefreshAndWriteLog(string logStr, bool warnflag = false)
|
|
|
|
|
{
|
|
|
|
|
TimeSpan currentTime = DateTime.Now.TimeOfDay;
|
|
|
|
|
// DateTime time = System.DateTime.Now;
|
|
|
|
|
string timeString = currentTime.ToString(@"hh\:mm\:ss");
|
|
|
|
|
BindingInfo = timeString + ":" + logStr;
|
|
|
|
|
if (warnflag)
|
|
|
|
|
{
|
|
|
|
|
MsgUIColor = "Red";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
MsgUIColor = "White";
|
|
|
|
|
}
|
|
|
|
|
logHelper.Info(BindingInfo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region 参数定义
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 提示信息-颜色
|
|
|
|
|
/// </summary>
|
|
|
|
|
public string msgUIColor;
|
|
|
|
|
public string MsgUIColor
|
|
|
|
|
{
|
|
|
|
|
get { return msgUIColor; }
|
|
|
|
|
|
|
|
|
|
set { msgUIColor = value; RaisePropertyChanged(nameof(MsgUIColor)); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 箱壳码
|
|
|
|
|
/// </summary>
|
|
|
|
|
private string code1 = string.Empty;
|
|
|
|
|
public string Code1
|
|
|
|
|
{
|
|
|
|
|
get { return code1; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
code1 = value;
|
|
|
|
|
RaisePropertyChanged(nameof(Code1));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 条码1扫描时间
|
|
|
|
|
/// </summary>
|
|
|
|
|
private string code1Time = string.Empty;
|
|
|
|
|
public string Code1Time
|
|
|
|
|
{
|
|
|
|
|
get { return code1Time; }
|
|
|
|
|
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
code1Time = value;
|
|
|
|
|
RaisePropertyChanged(nameof(Code1Time));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 内胆码
|
|
|
|
|
/// </summary>
|
|
|
|
|
private string code2 = string.Empty;
|
|
|
|
|
public string Code2
|
|
|
|
|
{
|
|
|
|
|
get { return code2; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
code2 = value;
|
|
|
|
|
RaisePropertyChanged(nameof(Code2));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
///// <summary>
|
|
|
|
|
///// 条码2扫描时间
|
|
|
|
|
///// </summary>
|
|
|
|
|
private string code2Time = string.Empty;
|
|
|
|
|
public string Code2Time
|
|
|
|
|
{
|
|
|
|
|
get { return code2Time; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
code2Time = value;
|
|
|
|
|
RaisePropertyChanged(nameof(Code2Time));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 绑定提示信息
|
|
|
|
|
/// </summary>
|
|
|
|
|
private string bindingInfo = string.Empty;
|
|
|
|
|
public string BindingInfo
|
|
|
|
|
{
|
|
|
|
|
get { return bindingInfo; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
bindingInfo = value;
|
|
|
|
|
RaisePropertyChanged(nameof(BindingInfo));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region 日产量柱状图X轴日期
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 日产量柱状图X轴日期
|
|
|
|
|
/// </summary>
|
|
|
|
|
private List<string> productionHourList = new List<string>();
|
|
|
|
|
|
|
|
|
|
public List<string> ProductionHourList
|
|
|
|
|
{
|
|
|
|
|
get { return productionHourList; }
|
|
|
|
|
set { productionHourList = value; }
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region 型号统计柱状图
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 型号统计柱状图
|
|
|
|
|
/// </summary>
|
|
|
|
|
private SeriesCollection modelStatistics = new SeriesCollection();
|
|
|
|
|
|
|
|
|
|
public SeriesCollection ModelStatistics
|
|
|
|
|
{
|
|
|
|
|
get { return modelStatistics; }
|
|
|
|
|
set { modelStatistics = value; }
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region 初始化datagrid
|
|
|
|
|
private ObservableCollection<ReaderInfo> listItems = new ObservableCollection<ReaderInfo>() { };
|
|
|
|
|
public ObservableCollection<ReaderInfo> ListItems
|
|
|
|
|
{
|
|
|
|
|
get { return listItems; }
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
listItems = value;
|
|
|
|
|
RaisePropertyChanged();//属性通知
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 修改为统计近一天白班或夜班
|
|
|
|
|
private async void LoadCharts()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
List<CodeBindCharts> list = _codeBindingRecordServices.QueryCharts().Result;
|
|
|
|
|
if (list == null || list.Count == 0) return;
|
|
|
|
|
|
|
|
|
|
var result = from m in list
|
|
|
|
|
group m by GetSubstringBetweenCommas(m.BoxName) into g
|
|
|
|
|
select new CodeBindCharts()
|
|
|
|
|
{
|
|
|
|
|
BoxName = g.Key,
|
|
|
|
|
Amount = g.Sum(m => m.Amount)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
App.Current.Dispatcher.Invoke(() =>
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
ProductionHourList.Clear();
|
|
|
|
|
ModelStatistics.Clear();
|
|
|
|
|
// 图表赋值
|
|
|
|
|
ChartValues<ObservablePoint> achievement = new ChartValues<ObservablePoint>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
|
double total = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var item in result)
|
|
|
|
|
{
|
|
|
|
|
ProductionHourList.Add(item.BoxName);
|
|
|
|
|
|
|
|
|
|
achievement.Add(new ObservablePoint(i, item.Amount));
|
|
|
|
|
total += item.Amount;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
//加一个汇总柱状图
|
|
|
|
|
ProductionHourList.Add("合计");
|
|
|
|
|
achievement.Add(new ObservablePoint(i, total));
|
|
|
|
|
|
|
|
|
|
var column = new ColumnSeries();
|
|
|
|
|
column.DataLabels = true;
|
|
|
|
|
column.Title = "型号";
|
|
|
|
|
column.Values = achievement;
|
|
|
|
|
column.Foreground = Brushes.White;
|
|
|
|
|
ModelStatistics.Add(column);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
catch (Exception)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 截取两个逗号之间的字符串
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="input"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
static string GetSubstringBetweenCommas(string input)
|
|
|
|
|
{
|
|
|
|
|
if (input == null) return null;
|
|
|
|
|
// 找到第一个逗号的位置
|
|
|
|
|
int firstCommaIndex = input.IndexOf(',');
|
|
|
|
|
if (firstCommaIndex != -1)
|
|
|
|
|
{
|
|
|
|
|
// 找到第二个逗号的位置
|
|
|
|
|
int secondCommaIndex = input.IndexOf(',', firstCommaIndex + 1);
|
|
|
|
|
if (secondCommaIndex != -1)
|
|
|
|
|
{
|
|
|
|
|
// 使用Substring截取第一个逗号和第二个逗号之间的字符
|
|
|
|
|
return input.Substring(firstCommaIndex + 1, secondCommaIndex - firstCommaIndex - 1);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public async void LoadData()
|
|
|
|
|
{
|
|
|
|
|
await Task.Run(() =>
|
|
|
|
|
{
|
|
|
|
|
List<CodeBindingRecord> list = _codeBindingRecordServices.QueryByTime();
|
|
|
|
|
App.Current.Dispatcher.Invoke(() =>
|
|
|
|
|
{
|
|
|
|
|
ListItems.Clear();
|
|
|
|
|
foreach (CodeBindingRecord record in list)
|
|
|
|
|
{
|
|
|
|
|
ListItems.Insert(0, new ReaderInfo()
|
|
|
|
|
{
|
|
|
|
|
No = ListItems.Count + 1,
|
|
|
|
|
ShellCode = record.BoxCode,
|
|
|
|
|
LinerCode = record.LinerCode,
|
|
|
|
|
BindingResult = record.BindingResult,
|
|
|
|
|
RecordTime1 = record.RecordTime1.ToString()
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 条码绑定
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="code1">MES码</param>
|
|
|
|
|
/// <param name="code2">SN码</param>
|
|
|
|
|
/// <param name="flag">1为自动扫描,2为手动扫描</param>
|
|
|
|
|
public void BindingCode(string code1, string code2)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
bool bindResult = false;
|
|
|
|
|
if (code1.Length != 21 || code2.Length != 21)
|
|
|
|
|
{
|
|
|
|
|
speechStr.SpeakAsync($"{code1.Substring(18)}扫码失败");
|
|
|
|
|
RefreshAndWriteLog("条码位数不对,扫描错误");
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RefreshAndWriteLog("开始绑定箱体码:" + code1 + "内胆码:" + code2);
|
|
|
|
|
|
|
|
|
|
BaseMaterialInfo materialInfo = _materialInfoService.GetMaterialInfoByType(code1.Substring(7, 10));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//// 2.查询条码绑定记录表(如果有不再重复插入)
|
|
|
|
|
int Repeatflag = 0;
|
|
|
|
|
CodeBindingRecord record = _codeBindingRecordServices.QueryByShell(shellCode);
|
|
|
|
|
if (record == null)
|
|
|
|
|
{
|
|
|
|
|
RefreshAndWriteLog($"开始绑定壳胆,箱体码{shellCode},内胆码{linerCode}");
|
|
|
|
|
bindResult = _codeBindingRecordServices.BindingCode(shellCode, linerCode,materialInfo);
|
|
|
|
|
}
|
|
|
|
|
else // 更新
|
|
|
|
|
{
|
|
|
|
|
RefreshAndWriteLog($"更新绑定壳胆,箱体码{shellCode},内胆码{linerCode}");
|
|
|
|
|
if (materialInfo == null)
|
|
|
|
|
{
|
|
|
|
|
record.BoxName = "";
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
record.BoxName = materialInfo.MaterialName;
|
|
|
|
|
}
|
|
|
|
|
record.BoxCode = shellCode;
|
|
|
|
|
record.LinerCode = linerCode;
|
|
|
|
|
record.RecordTime1 = DateTime.Now;
|
|
|
|
|
record.BindingResult = "成功";
|
|
|
|
|
record.Position1 = 1;
|
|
|
|
|
bindResult = _codeBindingRecordServices.Update(record);
|
|
|
|
|
}
|
|
|
|
|
if (bindResult)
|
|
|
|
|
{
|
|
|
|
|
RefreshAndWriteLog($"箱体码:{shellCode}与SN码:{linerCode} 绑定成功");
|
|
|
|
|
speechStr.SpeakAsync("绑定成功");
|
|
|
|
|
|
|
|
|
|
#region 更新mes完成计划
|
|
|
|
|
List<RealTaskInfo> shellTasks = GetTaskInfoByTaskStatus(appConfig.shellStoreCode, code1.Substring(7, 10), 3);
|
|
|
|
|
if (shellTasks!=null && shellTasks.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
shellTasks = shellTasks.OrderByDescending(x => x.createTime).ToList();
|
|
|
|
|
RealTaskInfo shellTask = shellTasks.First();
|
|
|
|
|
outStoreBusiness.UpdatePlanInfo(shellTask.planCode);
|
|
|
|
|
_taskInfoService.DeleteTaskInfoById(shellTask.objId);
|
|
|
|
|
RefreshChartsEvent?.Invoke();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
LoadData();
|
|
|
|
|
LoadCharts();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
RefreshAndWriteLog($"箱体码:{shellCode}与SN码:{linerCode} 绑定失败");
|
|
|
|
|
speechStr.SpeakAsync($"{shellCode.Substring(18)}绑定失败");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
logHelper.Error($"绑定异常{ex.Message}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 根据任务状态获取执行中的任务
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="storeCode"></param>
|
|
|
|
|
/// <param name="taskStatus"></param>s
|
|
|
|
|
private List<RealTaskInfo> GetTaskInfoByTaskStatus(string storeCode,string materialType, int taskStatus = 3)
|
|
|
|
|
{
|
|
|
|
|
List<RealTaskInfo> result = null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
result = _taskInfoService.GetTaskInfosByTaskStatus(new string[] { storeCode },2, taskStatus);
|
|
|
|
|
result = result.Where(x=>x.materialType == materialType).ToList();
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
logHelper.Error("根据任务状态获取执行中的任务异常", ex);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|