|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Data;
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Windows.Forms;
|
|
|
|
|
using Mesnac.Compressor.Data;
|
|
|
|
|
using Mesnac.Action.Default.Purview;
|
|
|
|
|
using SocketProcess;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using Mesnac.Compressor.Entity;
|
|
|
|
|
using Mesnac.Log;
|
|
|
|
|
using KeyBord;
|
|
|
|
|
|
|
|
|
|
namespace DisassemblyTable
|
|
|
|
|
{
|
|
|
|
|
public partial class Disassembly : Form
|
|
|
|
|
{
|
|
|
|
|
SerialEquip Com;
|
|
|
|
|
SocketServer socket;
|
|
|
|
|
string Port= System.Configuration.ConfigurationManager.AppSettings["para"];
|
|
|
|
|
string connType= System.Configuration.ConfigurationManager.AppSettings["Type"];
|
|
|
|
|
public delegate void Transit(string data);
|
|
|
|
|
public string NGStationID = "";
|
|
|
|
|
private ScanerHook_V2 listener;
|
|
|
|
|
public Disassembly()
|
|
|
|
|
{
|
|
|
|
|
InitializeComponent();
|
|
|
|
|
InitPort();
|
|
|
|
|
//initCombox();
|
|
|
|
|
//USB扫码枪
|
|
|
|
|
initUsbScanBarCode();
|
|
|
|
|
listener.Start();
|
|
|
|
|
InitializeInputLanguage("en-US");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
public void initUsbScanBarCode()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
listener = new ScanerHook_V2();
|
|
|
|
|
listener.ScanerEvent += Listener_ScanerEvent;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//函数内容: 函数方法,设置系统输入法
|
|
|
|
|
//函数版本: 0.0.0.0
|
|
|
|
|
//修改时间: 2023.07.12
|
|
|
|
|
//修改人:gyc
|
|
|
|
|
//============================================================================================
|
|
|
|
|
//注意事项:
|
|
|
|
|
//1.语言项,如zh-CN,en-US
|
|
|
|
|
//2.
|
|
|
|
|
//============================================================================================
|
|
|
|
|
private void InitializeInputLanguage(string cultureType)
|
|
|
|
|
{
|
|
|
|
|
//获取系统中已经安装的文字输入法
|
|
|
|
|
InputLanguageCollection MyInputs = InputLanguage.InstalledInputLanguages;
|
|
|
|
|
//遍历获取英文输入法索引值
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < MyInputs.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
if (MyInputs[i].Culture.IetfLanguageTag == cultureType)
|
|
|
|
|
{
|
|
|
|
|
//获取英文输入法
|
|
|
|
|
InputLanguage enInput = InputLanguage.InstalledInputLanguages[i];
|
|
|
|
|
//设置为当前输入法
|
|
|
|
|
InputLanguage.CurrentInputLanguage = enInput;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void Listener_ScanerEvent(ScanerHook_V2.ScanerCodes codes)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
this.textBox1.Text = "";
|
|
|
|
|
this.textBox1.Text = codes.Result;
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void InitPort()
|
|
|
|
|
{
|
|
|
|
|
if (connType.ToLower() == "socket")
|
|
|
|
|
{
|
|
|
|
|
socket = new SocketServer();
|
|
|
|
|
ThreadPool.QueueUserWorkItem(new WaitCallback(Thread), Port);
|
|
|
|
|
socket.ReciveEvent += socketRecieve;
|
|
|
|
|
}
|
|
|
|
|
else if (connType.ToLower() == "com")
|
|
|
|
|
{
|
|
|
|
|
// string Port = "COM3";
|
|
|
|
|
Com = new SerialEquip(connType+Port);
|
|
|
|
|
Com.OpenCom(null);
|
|
|
|
|
Com.SerialData += Receive;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
MessageBox.Show("未找到通信类型:"+ connType);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Thread(object o)
|
|
|
|
|
{
|
|
|
|
|
int port=Convert.ToInt32(o);
|
|
|
|
|
socket.StartService(port);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void socketRecieve(string barcode, object o)
|
|
|
|
|
{
|
|
|
|
|
Receive(barcode);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void Receive(string Data)
|
|
|
|
|
{
|
|
|
|
|
if (this.InvokeRequired)
|
|
|
|
|
{
|
|
|
|
|
this.Invoke(new Transit(Receive), Data);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
Console.WriteLine("扫描数据" + Data + "数据完成");
|
|
|
|
|
ClearAllControl();
|
|
|
|
|
if (!Data.Trim().Contains("E") && Data.Length < 10)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
//判断条码内容为什么组件(动静盘,前盖,壳体)
|
|
|
|
|
if (Data.Trim().Contains("M"))
|
|
|
|
|
{
|
|
|
|
|
//解绑
|
|
|
|
|
|
|
|
|
|
//通过条码查询是什么组件
|
|
|
|
|
DbHandler dbHandler = new DbHandler();
|
|
|
|
|
string remark = "";
|
|
|
|
|
if (radioButton1.Checked)
|
|
|
|
|
{
|
|
|
|
|
remark = radioButton1.Text;
|
|
|
|
|
}
|
|
|
|
|
else if (radioButton2.Checked)
|
|
|
|
|
{
|
|
|
|
|
remark = radioButton2.Text;
|
|
|
|
|
}
|
|
|
|
|
else if (radioButton3.Checked)
|
|
|
|
|
{
|
|
|
|
|
remark = radioButton3.Text;
|
|
|
|
|
}
|
|
|
|
|
else if (radioButton4.Checked)
|
|
|
|
|
{
|
|
|
|
|
remark = radioButton4.Text;
|
|
|
|
|
}
|
|
|
|
|
else if (radioButton5.Checked)
|
|
|
|
|
{
|
|
|
|
|
remark = radioButton5.Text;
|
|
|
|
|
}
|
|
|
|
|
dbHandler.InsertRepairInfo(Data, Data, "2", remark);
|
|
|
|
|
LogService.Instance.Error("》》》重投成功,产品码:" + textBox1.Text.Trim() + ",重投工位:2");
|
|
|
|
|
}
|
|
|
|
|
textBox1.Text = Data.Trim();
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} //cb_qiangai.Enabled = false; cb_zhuanzi.Enabled = false; cb_allRestart.Enabled = false;
|
|
|
|
|
|
|
|
|
|
//private void button1_Click(object sender, EventArgs e)
|
|
|
|
|
//{
|
|
|
|
|
// try
|
|
|
|
|
// {
|
|
|
|
|
// if (string.IsNullOrEmpty(textBox1.Text))
|
|
|
|
|
// {
|
|
|
|
|
// MessageBox.Show("条码为空!", "提示!");
|
|
|
|
|
// return;
|
|
|
|
|
// }
|
|
|
|
|
// DbHandler db = new DbHandler();
|
|
|
|
|
// int state1 = 0, state2 = 0, state3 = 0, state4 = 0;
|
|
|
|
|
// string RestartStation = "";
|
|
|
|
|
// string checker = UserInfo.Instance.UserName;
|
|
|
|
|
// if (cb_qiangai.Enabled == true)
|
|
|
|
|
// {
|
|
|
|
|
// if (cb_qiangai.CheckState == CheckState.Checked)
|
|
|
|
|
// {
|
|
|
|
|
// state1 = 1;
|
|
|
|
|
// }
|
|
|
|
|
// else
|
|
|
|
|
// {
|
|
|
|
|
// state1 = 0;
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// if (cb_zhuanzi.Enabled == true)
|
|
|
|
|
// {
|
|
|
|
|
// if (cb_zhuanzi.CheckState == CheckState.Checked)
|
|
|
|
|
// {
|
|
|
|
|
// state2 = 1;
|
|
|
|
|
// }
|
|
|
|
|
// else
|
|
|
|
|
// {
|
|
|
|
|
// state2 = 0;
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// if (cb_allRestart.Enabled == true)
|
|
|
|
|
// {
|
|
|
|
|
// if (cb_allRestart.CheckState == CheckState.Checked)
|
|
|
|
|
// {
|
|
|
|
|
// state3 = 1;
|
|
|
|
|
// RestartStation = comboBox1.SelectedValue.ToString();
|
|
|
|
|
// //RestartStation = NGStationID;
|
|
|
|
|
// }
|
|
|
|
|
// else
|
|
|
|
|
// {
|
|
|
|
|
// state3 = 0;
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// if (cb_AllScrapped.Checked)
|
|
|
|
|
// {
|
|
|
|
|
// state4 = 1;
|
|
|
|
|
// }
|
|
|
|
|
// if (db.Test(textBox1.Text, state1, state2, state3, state4, RestartStation, checker))
|
|
|
|
|
// {
|
|
|
|
|
// MessageBox.Show("拆解成功!", "提示!");
|
|
|
|
|
// ClearAllControl();
|
|
|
|
|
// }
|
|
|
|
|
// else
|
|
|
|
|
// {
|
|
|
|
|
// MessageBox.Show("拆解失败!", "提示!");
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
// catch(Exception ex)
|
|
|
|
|
// {
|
|
|
|
|
// //MessageBox.Show(ex.ToString());
|
|
|
|
|
// }
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
private void ClearAllControl()
|
|
|
|
|
{
|
|
|
|
|
this.textBox1.Text = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Disassembly_FormClosing(object sender, FormClosingEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
listener.Stop();
|
|
|
|
|
if (connType.ToLower() == "socket")
|
|
|
|
|
{
|
|
|
|
|
//socket.StopService();
|
|
|
|
|
socket.ReciveEvent -= socketRecieve;
|
|
|
|
|
}
|
|
|
|
|
else if (connType.ToLower() == "com")
|
|
|
|
|
{
|
|
|
|
|
if(Com != null)
|
|
|
|
|
Com.Close();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
private DataTable GetNewDatatable()
|
|
|
|
|
{
|
|
|
|
|
DataTable dt = new DataTable();
|
|
|
|
|
dt.Columns.Add("NG工位",typeof(string));
|
|
|
|
|
dt.Columns.Add("记录时间", typeof(string));
|
|
|
|
|
dt.Columns.Add("漏率显示", typeof(string));
|
|
|
|
|
dt.Columns.Add("漏率显示最小值", typeof(string));
|
|
|
|
|
dt.Columns.Add("漏率显示最大值", typeof(string));
|
|
|
|
|
dt.Columns.Add("漏率显示状态", typeof(string));
|
|
|
|
|
dt.Columns.Add("漏率显示幂值", typeof(string));
|
|
|
|
|
dt.Columns.Add("漏率显示幂值最小值", typeof(string));
|
|
|
|
|
dt.Columns.Add("漏率显示幂值最大值", typeof(string));
|
|
|
|
|
dt.Columns.Add("漏率显示幂值状态", typeof(string));
|
|
|
|
|
dt.Columns.Add("真空压力", typeof(string));
|
|
|
|
|
dt.Columns.Add("真空压力最小值", typeof(string));
|
|
|
|
|
dt.Columns.Add("真空压力最大值", typeof(string));
|
|
|
|
|
dt.Columns.Add("真空压力状态", typeof(string));
|
|
|
|
|
DataRow dataRow = dt.NewRow();
|
|
|
|
|
dataRow["NG工位"] = "140氦气检漏";
|
|
|
|
|
dataRow["记录时间"] = DateTime.Now.ToString("yyyy/MM/dd");
|
|
|
|
|
dataRow["漏率显示"] = "0.0000";
|
|
|
|
|
dataRow["漏率显示最小值"] = "0.0000";
|
|
|
|
|
dataRow["漏率显示最大值"] = "0.0000";
|
|
|
|
|
dataRow["漏率显示状态"] = "不合格";
|
|
|
|
|
dataRow["漏率显示幂值"] = "0.0000";
|
|
|
|
|
dataRow["漏率显示幂值最小值"] = "0.0000";
|
|
|
|
|
dataRow["漏率显示幂值最大值"] = "0.0000";
|
|
|
|
|
dataRow["漏率显示幂值状态"] = "不合格";
|
|
|
|
|
dataRow["真空压力"] = "0.0000";
|
|
|
|
|
dataRow["真空压力最小值"] = "0.0000";
|
|
|
|
|
dataRow["真空压力最大值"] = "0.0000";
|
|
|
|
|
dataRow["真空压力状态"] = "不合格";
|
|
|
|
|
dt.Rows.Add(dataRow);
|
|
|
|
|
return dt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void button1_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
string Data = "";
|
|
|
|
|
//ClearAllControl();
|
|
|
|
|
Data=textBox1.Text.Trim();
|
|
|
|
|
//Data = "SJ1811020005";
|
|
|
|
|
Console.WriteLine("扫描数据"+ Data);
|
|
|
|
|
DbHandler db = new DbHandler();
|
|
|
|
|
DataSet ds = db.Select(Data);
|
|
|
|
|
|
|
|
|
|
if (ds==null||ds.Tables.Count < 1)
|
|
|
|
|
{
|
|
|
|
|
//MessageBox.Show("未查询到当前条码生产NG信息");
|
|
|
|
|
//this.tb_ngstation.Text = "140氦气检漏";
|
|
|
|
|
DataTable dt = GetNewDatatable();
|
|
|
|
|
//dataGridView1.DataSource = dt;
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DataTable dt1 = ds.Tables[0];
|
|
|
|
|
if (dt1 != null && dt1.Rows.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
DataRow dr = dt1.Rows[0];
|
|
|
|
|
string NGstation = dr["StationName"].ToString();
|
|
|
|
|
//this.tb_ngstation.Text = NGstation;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//MessageBox.Show("未查询到当前条码生产NG信息");
|
|
|
|
|
//this.tb_ngstation.Text = "140氦气检漏";
|
|
|
|
|
DataTable dt = GetNewDatatable();
|
|
|
|
|
//dataGridView1.DataSource = dt;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (ds.Tables.Count > 1)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
//dataGridView1.DataSource = ds.Tables[1];
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
//for (int i = 0; i < dataGridView1.Columns.Count; i++)
|
|
|
|
|
//{
|
|
|
|
|
// dataGridView1.Columns[i].Frozen = false;
|
|
|
|
|
//}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void BindReStartStationInfo()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
DbHandler db = new DbHandler();
|
|
|
|
|
//cbReStartStation.DisplayMember = "StationName";
|
|
|
|
|
//cbReStartStation.ValueMember = "StationID";
|
|
|
|
|
//cbReStartStation.DataSource = db.GetReStartStationList();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
private void button2_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (textBox1.Text.Trim() == "")
|
|
|
|
|
{
|
|
|
|
|
MessageBox.Show("条码为空!", "提示!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
string REMARK = "";
|
|
|
|
|
DbHandler dbHandler = new DbHandler();
|
|
|
|
|
if (radioButton1.Checked)
|
|
|
|
|
{
|
|
|
|
|
REMARK = radioButton1.Text;
|
|
|
|
|
}else if (radioButton2.Checked)
|
|
|
|
|
{
|
|
|
|
|
REMARK = radioButton2.Text;
|
|
|
|
|
}else if (radioButton3.Checked)
|
|
|
|
|
{
|
|
|
|
|
REMARK = radioButton3.Text;
|
|
|
|
|
}else if (radioButton4.Checked)
|
|
|
|
|
{
|
|
|
|
|
REMARK = radioButton4.Text;
|
|
|
|
|
}
|
|
|
|
|
else if (radioButton5.Checked)
|
|
|
|
|
{
|
|
|
|
|
REMARK = radioButton5.Text;
|
|
|
|
|
}
|
|
|
|
|
dbHandler.InsertRepairInfo(textBox1.Text, textBox1.Text.Trim(),"2", REMARK);
|
|
|
|
|
//这里要判断下是否存在条码
|
|
|
|
|
//bool isHaveData = dbHandler.IsHaveTraceState(textBox1.Text.Trim());
|
|
|
|
|
//数据库里不存在该记录,有可能转盘上线工位没绑定
|
|
|
|
|
//string MBarCode = "";
|
|
|
|
|
//if (!isHaveData)
|
|
|
|
|
//{
|
|
|
|
|
// //手动插入一条记录
|
|
|
|
|
// //tb_ngstation.Text = "未查询到当前条码生产信息!";
|
|
|
|
|
// //生成M码A码
|
|
|
|
|
// string Asemibacode = dbHandler.CreatNewSemibarcode();
|
|
|
|
|
// MBarCode = dbHandler.GetLastMainBarcode("M");
|
|
|
|
|
// string machineID = dbHandler.GetNowProductionInfo();
|
|
|
|
|
// dbHandler.InsertTrace(MBarCode, Asemibacode, textBox1.Text.Trim(), machineID);
|
|
|
|
|
|
|
|
|
|
//}
|
|
|
|
|
//修改重投字段为2
|
|
|
|
|
//更新重投工位字段
|
|
|
|
|
|
|
|
|
|
//bool iflag = dbHandler.UpdateTraceReStartStation("2", cbReStartStation.SelectedValue.ToString(), textBox1.Text.Trim());
|
|
|
|
|
//if (iflag)
|
|
|
|
|
//{
|
|
|
|
|
// //插入返修记录
|
|
|
|
|
// dbHandler.InsertRepairInfo(MBarCode, textBox1.Text.Trim(), cbReStartStation.SelectedValue.ToString());
|
|
|
|
|
// LogService.Instance.Error("》》》重投成功,产品码:"+ textBox1.Text.Trim() + ",重投工位:"+ cbReStartStation.SelectedValue.ToString());
|
|
|
|
|
|
|
|
|
|
// //修改重投状态T_RP_SemiProInfo
|
|
|
|
|
// textBox1.Text = "";
|
|
|
|
|
// MessageBox.Show("重投工位设定成功!", "系统提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
|
|
|
|
|
|
|
|
|
//}
|
|
|
|
|
//else
|
|
|
|
|
//{
|
|
|
|
|
// MessageBox.Show("重投工位设定失败!", "系统提示", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
|
|
|
|
// LogService.Instance.Error("重投工位设定失败");
|
|
|
|
|
//}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
LogService.Instance.Error("异常:" + ex.Message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Disassembly_Load(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
//BindReStartStationInfo();
|
|
|
|
|
//dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
|
|
|
|
|
}
|
|
|
|
|
private void button3_Click(object sender, EventArgs e)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
//修改数据
|
|
|
|
|
//查询条码
|
|
|
|
|
DbHandler db = new DbHandler();
|
|
|
|
|
DataTable dt = db.Test1();
|
|
|
|
|
if (dt != null && dt.Rows.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < dt.Rows.Count; i++)
|
|
|
|
|
{
|
|
|
|
|
//这里如果要判断最后一工位与倒数第二个工位互锁,取ID=16
|
|
|
|
|
string s = dt.Rows[i]["SemiBarcode_A"].ToString();
|
|
|
|
|
LogService.Instance.Debug("A码:" + s);
|
|
|
|
|
//按照A码查询产品码
|
|
|
|
|
string proBarcode = db.GetProductionInfo(s);
|
|
|
|
|
LogService.Instance.Debug("产品码:" + proBarcode);
|
|
|
|
|
//执行update语句
|
|
|
|
|
db.UpdateCPM(proBarcode, s);
|
|
|
|
|
|
|
|
|
|
LogService.Instance.Debug((i + 1) + "行,更新成功!");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
LogService.Instance.Error("异常:" + ex.Message);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|