|
|
@@ -40,6 +40,8 @@ using LampInspectionMachine.Cameralibs.HKCamera;
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
using OpenCvSharp.Dnn;
|
|
|
using LampInspectionMachine.Interfaces;
|
|
|
+using System.IO;
|
|
|
+using LampInspectionMachine.KHLplc;
|
|
|
|
|
|
namespace LampInspectionMachine.Core
|
|
|
{
|
|
|
@@ -50,25 +52,17 @@ namespace LampInspectionMachine.Core
|
|
|
|
|
|
|
|
|
private CameraView cameraView;
|
|
|
- private ICogImage image = new CogImage8Grey();
|
|
|
- private WriteableBitmap writeable;
|
|
|
-
|
|
|
- public WriteableBitmap Writeable { get => writeable; set { SetProperty(ref writeable, value); } }
|
|
|
- public ICogImage Image
|
|
|
+ private ShowRender render = new ShowRender();
|
|
|
+ public ShowRender Render
|
|
|
{
|
|
|
- get => image;
|
|
|
+ get => render;
|
|
|
set
|
|
|
{
|
|
|
- SetProperty(ref image, value);
|
|
|
+ SetProperty(ref render, value);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- // 使用泛型委托
|
|
|
- public event EventHandler<ICogImage> PropertyChanged;
|
|
|
-
|
|
|
- // 或者自定义委托
|
|
|
- public delegate void ChangeEventHandler(ICogImage e);
|
|
|
- public event ChangeEventHandler OnChange;
|
|
|
+ private KHLCommunicate _kHLCommunicate;
|
|
|
+ public KHLCommunicate KHLCommunicate { get => _kHLCommunicate; set => _kHLCommunicate = value; }
|
|
|
|
|
|
public CameraService CameraService { get; set; }
|
|
|
|
|
|
@@ -82,12 +76,13 @@ namespace LampInspectionMachine.Core
|
|
|
/// <summary>
|
|
|
/// 当前相机配置
|
|
|
/// </summary>
|
|
|
- public CameraInfo CurrCamConfig { get => _CurrCamConfig; set { SetProperty(ref _CurrCamConfig, value); } }
|
|
|
+ public CameraInfo CurrCamConfig { get => _CurrCamConfig;
|
|
|
+ set { SetProperty(ref _CurrCamConfig, value); } }
|
|
|
|
|
|
/// <summary>
|
|
|
/// 相机配置列表
|
|
|
/// </summary>
|
|
|
- public ObservableCollection<CameraInfo> CamConfigs { get => _CamConfigs; set { SetProperty(ref _CamConfigs, value); } }
|
|
|
+ public List<CameraInfo> CamConfigs { get => _CamConfigs; set { SetProperty(ref _CamConfigs, value); } }
|
|
|
|
|
|
// public bool IsTriggerModel { get => _isTriggerModel; set { SetProperty(ref _isTriggerModel, value); } }
|
|
|
|
|
|
@@ -99,19 +94,29 @@ namespace LampInspectionMachine.Core
|
|
|
// public List<CogToolBlock> CamTemplateS { get => _CamTemplateS; set => _CamTemplateS = value; }
|
|
|
|
|
|
public event Action<int> ToolBlockSwitched;
|
|
|
+
|
|
|
+ public event Action<int> ToolBlockCatched;
|
|
|
+
|
|
|
+
|
|
|
+ private int _CurrIndex=0;
|
|
|
+ /// <summary>
|
|
|
+ /// 当前选中的视觉方案id
|
|
|
+ /// </summary>
|
|
|
+ public int CurrIndex { get => _CurrIndex; set => _CurrIndex = value; }
|
|
|
/// <summary>
|
|
|
/// 当前选中相机的SN
|
|
|
/// </summary>
|
|
|
public string CurrCameraSn { get => _CurrCameraSn;
|
|
|
set { SetProperty(ref _CurrCameraSn, value); } }
|
|
|
|
|
|
- public List<VisionProManager> VisionProManagers { get => _visionProManagers; set { SetProperty(ref _visionProManagers, value); } }
|
|
|
+ public ObservableCollection<VisionProManager> VisionProManagers { get => _visionProManagers; set { SetProperty(ref _visionProManagers, value); } }
|
|
|
|
|
|
public VisionProManager Currvision { get => _Currvision; set => _Currvision = value ; }
|
|
|
+
|
|
|
|
|
|
- private ObservableCollection<CameraInfo> _CamConfigs = new ObservableCollection<CameraInfo>();
|
|
|
+ private List<CameraInfo> _CamConfigs = new List<CameraInfo>();
|
|
|
|
|
|
- private List<VisionProManager> _visionProManagers=new List<VisionProManager>();
|
|
|
+ private ObservableCollection<VisionProManager> _visionProManagers=new ObservableCollection<VisionProManager>();
|
|
|
|
|
|
// private List<CogToolBlock> _CamTemplateS = new List<CogToolBlock>();
|
|
|
|
|
|
@@ -135,8 +140,13 @@ namespace LampInspectionMachine.Core
|
|
|
|
|
|
}
|
|
|
|
|
|
+ public async void ConnectPlc()
|
|
|
+ {
|
|
|
+ KHLCommunicate = new KHLCommunicate();
|
|
|
+ await KHLCommunicate.ConnPlc();
|
|
|
+ }
|
|
|
|
|
|
-
|
|
|
+
|
|
|
|
|
|
/// <summary>
|
|
|
/// 获取相机列表
|
|
|
@@ -145,6 +155,7 @@ namespace LampInspectionMachine.Core
|
|
|
{
|
|
|
CameraService = new CameraService();
|
|
|
CameraList = FindLoadCamera();
|
|
|
+ CamConfigs= FileHelper.ReadApplicationConfiguration();
|
|
|
if (CamConfigs.Count == 0)
|
|
|
{
|
|
|
CamConfigs.Add(new CameraInfo() { CameraName="相机1", Id=new Guid(), SerialNumber="", VppIndex=0, VppFileName = "Cam1.vpp" });
|
|
|
@@ -156,13 +167,14 @@ namespace LampInspectionMachine.Core
|
|
|
CamConfigs.Add(new CameraInfo() { CameraName = "相机7", Id = new Guid(), SerialNumber = "", VppIndex = 6, VppFileName = "Cam7.vpp" });
|
|
|
CamConfigs.Add(new CameraInfo() { CameraName = "相机8", Id = new Guid(), SerialNumber = "", VppIndex = 7, VppFileName = "Cam8.vpp" });
|
|
|
}
|
|
|
+ LogHelper.Info("加载配置文件完成");
|
|
|
}
|
|
|
|
|
|
|
|
|
public void SaveCameraDevice()
|
|
|
{
|
|
|
-
|
|
|
-
|
|
|
+ FileHelper.SaveApplicationConfiguration(CamConfigs);
|
|
|
+ LogHelper.Info("保存配置完成");
|
|
|
}
|
|
|
/// <summary>
|
|
|
/// 初始化相机管理
|
|
|
@@ -170,25 +182,34 @@ namespace LampInspectionMachine.Core
|
|
|
public void InitTemplates(TipService tipService,Management management)
|
|
|
{
|
|
|
tipService.ShowMsg("相机模板加载2%");
|
|
|
- for ( int i = 0; i < 1; i++ )
|
|
|
+ for ( int i = 0; i < CamConfigs.Count; i++ )
|
|
|
{
|
|
|
|
|
|
CogToolBlock cogToolBlock = (CogToolBlock)CogSerializer.LoadObjectFromFile(Environment.CurrentDirectory + $"\\Vpp\\{CamConfigs[i].VppFileName}");
|
|
|
VisionProManager vision=new VisionProManager();
|
|
|
ICamera myCamera;
|
|
|
myCamera = CreateCamera(CamConfigs[i]);
|
|
|
- if ( myCamera.IsConnected)
|
|
|
+ if (myCamera!=null&& myCamera.IsConnected)
|
|
|
{
|
|
|
- myCamera.ImageCallbackEvent += vision.CamCallBack;
|
|
|
- myCamera.GrabImageCallbackEvent += vision.ImageCallback;
|
|
|
+ if(myCamera.CheckImageCallbackEventIsHas(vision.CamCallBack))
|
|
|
+ myCamera.ImageCallbackEvent -= vision.CamCallBack;
|
|
|
+ if ( !myCamera.CheckGrabImageCallbackEventIsHas(vision.ImageCallback) )
|
|
|
+ myCamera.GrabImageCallbackEvent += vision.ImageCallback;
|
|
|
+ myCamera.CameraConnectChangedEvent += vision.CameraConnectChangedEvent;
|
|
|
+ vision.IsConnected = true;
|
|
|
}
|
|
|
vision.Camera= myCamera;
|
|
|
+ vision.PlcData.PlcToPcCodeIndex = (uint)(100 + i);
|
|
|
+ vision.PlcData.PcToPlcCompleted = (uint)(200 + i);
|
|
|
+ vision.PlcData.PcToPlcResult = (uint)(300 + i);
|
|
|
+ vision.PlcData.PcToPlcCodeIndex= (uint)(400 + i);
|
|
|
vision.CogToolBlock = cogToolBlock;
|
|
|
vision.CameraInfo = CamConfigs[ i ];
|
|
|
vision.Management=management;
|
|
|
VisionProManagers.Add(vision);
|
|
|
tipService.ShowMsg($"相机模板加载{(i+1)*10+2}%");
|
|
|
}
|
|
|
+ LogHelper.Info("模板加载完成");
|
|
|
tipService.Tcolse();
|
|
|
}
|
|
|
|
|
|
@@ -201,11 +222,17 @@ namespace LampInspectionMachine.Core
|
|
|
{
|
|
|
CurrCamera.StopGrabbing();
|
|
|
LogHelper.Info($"软触发配置写入");
|
|
|
- CurrCamera.SetTriggerMode(false,7) ;
|
|
|
+ CurrCamera.SetTriggerMode(false, 7);
|
|
|
+ if ( !CurrCamera.CheckImageCallbackEventIsHas(Currvision.CamCallBack) )
|
|
|
+ CurrCamera.ImageCallbackEvent += Currvision.CamCallBack;
|
|
|
CurrCamConfig.TriggerMode = false;
|
|
|
LogHelper.Info($"软触发取图");
|
|
|
CurrCamera.Grab();
|
|
|
}
|
|
|
+ else
|
|
|
+ {
|
|
|
+ System.Windows.Forms.MessageBox.Show("相机未打开");
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -213,30 +240,139 @@ namespace LampInspectionMachine.Core
|
|
|
/// 连续采集写入
|
|
|
/// </summary>
|
|
|
public void SoftTrigger__Continue()
|
|
|
- { CurrCamera.StopGrabbing();
|
|
|
- //MyCamera.SetTriggerModeOff();
|
|
|
- //CurrCamConfig.TriggerMode = false;
|
|
|
- //MyCamera.SetTriggerSoftware();
|
|
|
- //if (MyCamera.CheckImageCallbackEvent(CamCallBack))
|
|
|
- // MyCamera.ImageCallbackEvent += CamCallBack;
|
|
|
- //if (!MyCamera.CheckImageCallbackEvent(CamVisionProCallBack))
|
|
|
- // MyCamera.ImageCallbackEvent -= CamVisionProCallBack;
|
|
|
- LogHelper.Info($"连续采集启动");
|
|
|
- CurrCamera.StartGrabbing();
|
|
|
+ {
|
|
|
+ if ( CurrCamera!=null&& CurrCamera.IsConnected )
|
|
|
+ {
|
|
|
+ CurrCamera.StopGrabbing();
|
|
|
+ CurrCamera.SetTriggerMode(false, 7);
|
|
|
+ CurrCamConfig.TriggerMode = false;
|
|
|
+ if ( !CurrCamera.CheckImageCallbackEventIsHas(Currvision.CamCallBack) )
|
|
|
+ CurrCamera.ImageCallbackEvent += Currvision.CamCallBack;
|
|
|
+ LogHelper.Info($"连续采集启动");
|
|
|
+ CurrCamera.StartGrabbing();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ System.Windows.Forms.MessageBox.Show("相机未打开");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 单张图片测试
|
|
|
+ /// </summary>
|
|
|
+ public void ImageTrigger()
|
|
|
+ {
|
|
|
+ OpenFileDialog openFileDialog = new OpenFileDialog();
|
|
|
+ if ( openFileDialog.ShowDialog() == DialogResult.OK )
|
|
|
+ {
|
|
|
+ // 加载图像文件
|
|
|
+ using ( var bitmap = new Bitmap(openFileDialog.FileName) )
|
|
|
+ {
|
|
|
+ // 转换为VisionPro图像格式
|
|
|
+ ICogImage icogImage = new CogImage8Grey(bitmap);
|
|
|
+
|
|
|
+ // 设置输入参数
|
|
|
+ Currvision.CogToolBlock.Inputs[0].Value = icogImage;
|
|
|
+
|
|
|
+ // 执行处理
|
|
|
+ Currvision.CogToolBlock.Run();
|
|
|
+
|
|
|
+ // 获取输出结果
|
|
|
+ if (Currvision.CogToolBlock.RunStatus.Result == CogToolResultConstants.Accept)
|
|
|
+ {
|
|
|
+
|
|
|
+ CogToolBlockTerminalCollection outputCollection = Currvision.CogToolBlock.Outputs;
|
|
|
+ ICogRecord record = null;
|
|
|
+ foreach (CogToolBlockTerminal item in outputCollection)
|
|
|
+ {
|
|
|
+ if (item.Value is ICogRecord)
|
|
|
+ record = item.Value as ICogRecord;
|
|
|
+ }
|
|
|
+ ICogImage cogImage = (ICogImage)Currvision.CogToolBlock.Inputs["Image"].Value;
|
|
|
+ if (cogImage == null)
|
|
|
+ {
|
|
|
+
|
|
|
+ Render = new ShowRender()
|
|
|
+ {
|
|
|
+ Graphic = outputCollection["Graphic"].Value as CogGraphicCollection,
|
|
|
+ Image = icogImage,
|
|
|
+ Record = record
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Render = new ShowRender()
|
|
|
+ {
|
|
|
+
|
|
|
+ Image = icogImage
|
|
|
+
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// 图片轮询测试
|
|
|
+ /// </summary>
|
|
|
+ public async Task ImageTrigger__Continue()
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ FolderBrowserDialog openFileDialog = new FolderBrowserDialog();
|
|
|
+ if ( openFileDialog.ShowDialog() == DialogResult.OK )
|
|
|
+ {
|
|
|
+ DirectoryInfo folder = new DirectoryInfo(openFileDialog.SelectedPath);
|
|
|
+ FileInfo[] files = folder.GetFiles("*.bmp", SearchOption.AllDirectories);
|
|
|
+
|
|
|
+ Task.Run(() =>
|
|
|
+ {
|
|
|
+ foreach ( FileInfo file in files )
|
|
|
+ {
|
|
|
+ // 加载图像文件
|
|
|
+ using ( var bitmap = new Bitmap(file.FullName) )
|
|
|
+ {
|
|
|
+ // 转换为VisionPro图像格式
|
|
|
+ ICogImage cogImage = new CogImage8Grey(bitmap);
|
|
|
+
|
|
|
+ Currvision.ImageCallback(cogImage);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ });
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch
|
|
|
+ {
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
public void SetHardTrigger_CallBack()
|
|
|
{
|
|
|
- LogHelper.Info($"硬触发模式 启动");
|
|
|
- //MyCamera.SetTriggerModeOn();
|
|
|
- //CurrCamConfig.TriggerMode = true;
|
|
|
- //MyCamera.SetTriggerSoftware(0);
|
|
|
- //if (!MyCamera.CheckImageCallbackEvent(CamCallBack))
|
|
|
- // MyCamera.ImageCallbackEvent -= CamCallBack;
|
|
|
- //if (MyCamera.CheckImageCallbackEvent(CamVisionProCallBack))
|
|
|
- // MyCamera.ImageCallbackEvent += CamVisionProCallBack;
|
|
|
- CurrCamera.StartGrabbing();
|
|
|
+ if ( CurrCamera != null && CurrCamera.IsConnected )
|
|
|
+ {
|
|
|
+ LogHelper.Info($"硬触发模式 启动");
|
|
|
+ CurrCamera.ImageCallbackEvent -= Currvision.CamCallBack;
|
|
|
+ CurrCamera.SetTriggerMode(true, 0);
|
|
|
+ CurrCamConfig.TriggerMode = true;
|
|
|
+ CurrCamera.StartGrabbing();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+
|
|
|
+ System.Windows.Forms.MessageBox.Show("相机未打开");
|
|
|
+ }
|
|
|
}
|
|
|
/// <summary>
|
|
|
/// 查找相机
|
|
|
@@ -265,49 +401,63 @@ namespace LampInspectionMachine.Core
|
|
|
|
|
|
public bool InitCamera()
|
|
|
{
|
|
|
- ICamera myCamera;
|
|
|
- if ( CurrCamera!=null&& CurrCamera.IsConnected )
|
|
|
+ try
|
|
|
{
|
|
|
- CurrCamera.CloseDevice();
|
|
|
- myCamera = CameraService.GetCamera(CurrCamera.ID);
|
|
|
- bool res = myCamera.OpenDevice();
|
|
|
-
|
|
|
- if ( !res )
|
|
|
+ ICamera myCamera;
|
|
|
+ if ( CurrCamera != null && CurrCamera.IsConnected )
|
|
|
{
|
|
|
- myCamera.CloseDevice();
|
|
|
- myCamera = null;
|
|
|
- return false;
|
|
|
+ CurrCamera.CloseDevice();
|
|
|
+ myCamera = CameraService.GetCamera(CurrCamera.ID);
|
|
|
+ bool res = myCamera.OpenDevice();
|
|
|
+
|
|
|
+ if ( !res )
|
|
|
+ {
|
|
|
+ myCamera.CloseDevice();
|
|
|
+ myCamera = null;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ CurrCamera = myCamera;
|
|
|
+ CurrCamConfig.SerialNumber = myCamera.SerialNumber;
|
|
|
+ if ( myCamera.IsConnected )
|
|
|
+ {
|
|
|
+ if ( !myCamera.CheckImageCallbackEventIsHas(Currvision.CamCallBack) )
|
|
|
+ myCamera.ImageCallbackEvent += Currvision.CamCallBack;
|
|
|
+ if ( !myCamera.CheckGrabImageCallbackEventIsHas(Currvision.ImageCallback) )
|
|
|
+ myCamera.GrabImageCallbackEvent += Currvision.ImageCallback;
|
|
|
+ if ( !myCamera.CheckCameraConnectChangedEventIsHas(Currvision.CameraConnectChangedEvent) )
|
|
|
+ myCamera.CameraConnectChangedEvent += Currvision.CameraConnectChangedEvent;
|
|
|
+
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
+ CurrCamera = null;
|
|
|
+ myCamera = CreateCamera(new CameraInfo() { SerialNumber = CurrCameraSn });
|
|
|
CurrCamera = myCamera;
|
|
|
CurrCamConfig.SerialNumber = myCamera.SerialNumber;
|
|
|
- if ( myCamera.IsConnected )
|
|
|
+ if ( myCamera != null&& myCamera.IsConnected )
|
|
|
{
|
|
|
- myCamera.ImageCallbackEvent -= Currvision.CamCallBack;
|
|
|
- myCamera.GrabImageCallbackEvent -= Currvision.ImageCallback;
|
|
|
- myCamera.ImageCallbackEvent += Currvision.CamCallBack;
|
|
|
- myCamera.GrabImageCallbackEvent += Currvision.ImageCallback;
|
|
|
+ if ( !myCamera.CheckImageCallbackEventIsHas(Currvision.CamCallBack) )
|
|
|
+ myCamera.ImageCallbackEvent += Currvision.CamCallBack;
|
|
|
+ if ( !myCamera.CheckGrabImageCallbackEventIsHas(Currvision.ImageCallback) )
|
|
|
+ myCamera.GrabImageCallbackEvent += Currvision.ImageCallback;
|
|
|
+ if ( !myCamera.CheckCameraConnectChangedEventIsHas(Currvision.CameraConnectChangedEvent) )
|
|
|
+ myCamera.CameraConnectChangedEvent += Currvision.CameraConnectChangedEvent;
|
|
|
+ Currvision.IsConnected = true;
|
|
|
}
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
- else
|
|
|
+ catch
|
|
|
{
|
|
|
+ CurrCamera?.CloseDevice();
|
|
|
CurrCamera = null;
|
|
|
- myCamera=CreateCamera(new CameraInfo() { SerialNumber=CurrCameraSn });
|
|
|
- CurrCamera = myCamera;
|
|
|
- CurrCamConfig.SerialNumber = myCamera.SerialNumber;
|
|
|
- if ( myCamera.IsConnected )
|
|
|
- {
|
|
|
- myCamera.ImageCallbackEvent -= Currvision.CamCallBack;
|
|
|
- myCamera.GrabImageCallbackEvent -= Currvision.ImageCallback;
|
|
|
- myCamera.ImageCallbackEvent += Currvision.CamCallBack;
|
|
|
- myCamera.GrabImageCallbackEvent += Currvision.ImageCallback;
|
|
|
- }
|
|
|
- return true;
|
|
|
+ return false;
|
|
|
}
|
|
|
-
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -470,43 +620,17 @@ namespace LampInspectionMachine.Core
|
|
|
cogToolBlock1 = VisionProManagers[index].CogToolBlock;
|
|
|
CurrCamera= VisionProManagers[ index ].Camera;
|
|
|
CurrCamConfig = VisionProManagers[ index ].CameraInfo;
|
|
|
-
|
|
|
+
|
|
|
ToolBlockSwitched?.Invoke(index); // 触发事件,传递索引参数。
|
|
|
}
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 相机连接或者断开时
|
|
|
- /// </summary>
|
|
|
- /// <param name="id"></param>
|
|
|
- /// <param name="state"></param>
|
|
|
- private void Management_CameraConnectChangedEvent(Guid id, bool state)
|
|
|
+ public void OnSaveSwitchToolBlock(int index)
|
|
|
{
|
|
|
- // var sta = Status.FirstOrDefault(s => s.ID == id);
|
|
|
- // var camera = DeviceConfig.Cameras.FirstOrDefault(c => c.Id == id);
|
|
|
- //if ( sta != null && camera != null )
|
|
|
- //{
|
|
|
- // if ( state )
|
|
|
- // {
|
|
|
- // App.Current.Dispatcher.Invoke(new Action(() =>
|
|
|
- // {
|
|
|
- // sta.Message = $"{camera.CameraName}{connected}";
|
|
|
- // sta.Background = new SolidColorBrush(Colors.Green);
|
|
|
- // }));
|
|
|
-
|
|
|
- // }
|
|
|
- // else
|
|
|
- // {
|
|
|
- // App.Current.Dispatcher.Invoke(new Action(() =>
|
|
|
- // {
|
|
|
- // sta.Message = $"{camera.CameraName}{disconnected1}";
|
|
|
- // sta.Background = new SolidColorBrush(Colors.Red);
|
|
|
- // }));
|
|
|
- // }
|
|
|
- //}
|
|
|
+ ToolBlockCatched?.Invoke(index);
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
+
|
|
|
}
|
|
|
|
|
|
|