diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/InSsjThread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/bs/BsInSsjThread.java similarity index 96% rename from ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/InSsjThread.java rename to ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/bs/BsInSsjThread.java index 587c31c..e25b438 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/InSsjThread.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/bs/BsInSsjThread.java @@ -1,15 +1,15 @@ -package com.ruoyi.schedule.scheduleTask.ssj; +package com.ruoyi.schedule.scheduleTask.ssj.bs; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; -import com.ruoyi.device.agv.domain.AgvQueue; -import com.ruoyi.device.agv.service.IAgvQueueService; import com.ruoyi.api.domain.agv.genAgvSchedulingTask.GenAgvSchedulingTaskRequest; import com.ruoyi.api.domain.agv.genAgvSchedulingTask.GenAgvSchedulingTaskResponse; import com.ruoyi.api.domain.wcs2wms.MydoJob; import com.ruoyi.api.service.IApiService; +import com.ruoyi.device.agv.domain.AgvQueue; +import com.ruoyi.device.agv.service.IAgvQueueService; import com.ruoyi.device.ssj.domain.DeviceSsj; import com.ruoyi.schedule.plcRead.ssj.PlcRead; import lombok.extern.slf4j.Slf4j; @@ -20,12 +20,11 @@ import javax.annotation.Resource; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.concurrent.ConcurrentHashMap; @Slf4j @Component -public class InSsjThread { +public class BsInSsjThread { public HashMap> ssjCodeList; @@ -39,6 +38,10 @@ public class InSsjThread { public void init() { //todo 获取设备编码列表 ssjCodeList = new HashMap<>(); + ArrayList ssjCodes = new ArrayList<>(); + ssjCodes.add("G1003"); + ssjCodes.add("G1009"); + ssjCodeList.put("in", ssjCodes); } public void run() { diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/OutSsjThread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/bs/BsOutSsjThread.java similarity index 77% rename from ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/OutSsjThread.java rename to ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/bs/BsOutSsjThread.java index b8accc1..0c3f607 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/OutSsjThread.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/bs/BsOutSsjThread.java @@ -1,4 +1,4 @@ -package com.ruoyi.schedule.scheduleTask.ssj; +package com.ruoyi.schedule.scheduleTask.ssj.bs; import cn.hutool.core.collection.CollUtil; import com.ruoyi.device.agv.domain.AgvQueue; @@ -17,12 +17,11 @@ import javax.annotation.Resource; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.concurrent.ConcurrentHashMap; @Slf4j @Component -public class OutSsjThread { +public class BsOutSsjThread { public HashMap> ssjCodeList; @@ -34,25 +33,39 @@ public class OutSsjThread { @PostConstruct public void init() { - //todo 获取设备编码列表 ssjCodeList = new HashMap<>(); + ArrayList ssjCodes = new ArrayList<>(); + ssjCodes.add("G1004"); + ssjCodes.add("G1010"); + ssjCodeList.put("ssjOut", ssjCodes); } public void run() { for (String type : ssjCodeList.keySet()) { - if ("out".equals(type)) { - for (String ssjCode : ssjCodeList.get(type)) { - if (PlcRead.deviceMap.containsKey(ssjCode)) { - outDispatch(PlcRead.deviceMap.get(ssjCode)); - } else { - log.error("设备不存在 ssjCode{}", ssjCode); + switch (type){ + case "ssjOut": + //正常输送线出口 + for (String ssjCode : ssjCodeList.get(type)) { + if (PlcRead.deviceMap.containsKey(ssjCode)) { + ssjOutDispatch(PlcRead.deviceMap.get(ssjCode)); + } else { + log.error("设备不存在 ssjCode{}", ssjCode); + } } - } + break; + case "fluentOut": + //流利架出口 + break; } + } } - public void outDispatch(DeviceSsj outSsjDevice) { + /** + * 输送线出口 + * @param outSsjDevice + */ + public void ssjOutDispatch(DeviceSsj outSsjDevice) { //输送线出库 if (outSsjDevice.getKx()==1){ //查询对应的作业 @@ -78,8 +91,14 @@ public class OutSsjThread { log.info("ssjCode:{},没有待下发的出库任务", outSsjDevice.getDeviceCode()); } } + } - + /** + * 流利架出口 + * @param outSsjDevice + */ + public void fluentOutDispatch(DeviceSsj outSsjDevice) { + //查询流利架货位是否 } /** diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/PickSsjTread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/bs/BsPickSsjTread.java similarity index 91% rename from ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/PickSsjTread.java rename to ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/bs/BsPickSsjTread.java index 404cc62..1f754cc 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/PickSsjTread.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/bs/BsPickSsjTread.java @@ -1,4 +1,4 @@ -package com.ruoyi.schedule.scheduleTask.ssj; +package com.ruoyi.schedule.scheduleTask.ssj.bs; import cn.hutool.core.collection.CollUtil; import com.ruoyi.device.agv.service.IAgvQueueService; @@ -16,12 +16,11 @@ import javax.annotation.Resource; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.concurrent.ConcurrentHashMap; @Slf4j @Component -public class PickSsjTread { +public class BsPickSsjTread { public HashMap> ssjCodeList; @@ -33,8 +32,10 @@ public class PickSsjTread { @PostConstruct public void init() { - //todo 获取设备编码列表 ssjCodeList = new HashMap<>(); + ArrayList ssjCodes = new ArrayList<>(); + //流利架 + ssjCodeList.put("pick", ssjCodes); } diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/js/JsInSsjThread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/js/JsInSsjThread.java new file mode 100644 index 0000000..920a39b --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/js/JsInSsjThread.java @@ -0,0 +1,185 @@ +package com.ruoyi.schedule.scheduleTask.ssj.js; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.ruoyi.device.agv.domain.AgvQueue; +import com.ruoyi.device.agv.service.IAgvQueueService; +import com.ruoyi.api.domain.agv.genAgvSchedulingTask.GenAgvSchedulingTaskRequest; +import com.ruoyi.api.domain.agv.genAgvSchedulingTask.GenAgvSchedulingTaskResponse; +import com.ruoyi.api.domain.wcs2wms.MydoJob; +import com.ruoyi.api.service.IApiService; +import com.ruoyi.device.ssj.domain.DeviceSsj; +import com.ruoyi.schedule.plcRead.ssj.PlcRead; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + + +@Slf4j +@Component +public class JsInSsjThread { + + public HashMap> ssjCodeList; + + @Resource + private IApiService apiService; + @Resource + private IAgvQueueService agvQueueService; + + + @PostConstruct + public void init() { + //todo 获取设备编码列表 + ssjCodeList = new HashMap<>(); + ArrayList ssjCodes = new ArrayList<>(); + ssjCodes.add("G2012"); + ssjCodes.add("G3009"); + ssjCodes.add("G3018"); + ssjCodes.add("G4009"); + ssjCodes.add("G4018"); + ssjCodes.add("G5009"); + ssjCodes.add("G5018"); + ssjCodeList.put("in", ssjCodes); + } + + public void run() { + for (String type : ssjCodeList.keySet()) { + if ("in".equals(type)) { + for (String ssjCode : ssjCodeList.get(type)) { + if (PlcRead.deviceMap.containsKey(ssjCode)) { + inDispatch(PlcRead.deviceMap.get(ssjCode)); + } else { + log.error("设备不存在 ssjCode{}", ssjCode); + } + } + } + } + + } + + + public void inDispatch(DeviceSsj inSsjDevice) { + //如果入库请求1 并且 托盘码不为空 + if (inSsjDevice.getRkqq() == 1 && StrUtil.isNotEmpty(inSsjDevice.getTrayCode())) { + //根据入口,查询对应的入库作业 + MydoJob mydoJobParam = new MydoJob(); + //mydoJobParam.setEntranceId(inSsjDevice.getDeviceCode()); + mydoJobParam.setStatus("1"); + mydoJobParam.setTrayCode(inSsjDevice.getTrayCode()); + List mydoJobs = apiService.getMydoJobList(mydoJobParam); + if (CollUtil.isNotEmpty(mydoJobs)) { + MydoJob mydoJob = mydoJobs.get(0); + //修改mydojob入口 + mydoJob.setEntranceId(inSsjDevice.getDeviceCode()); + apiService.updateMydoJob(mydoJob); + + //判断是否生成队列 + AgvQueue agvQueueParam = new AgvQueue(); + agvQueueParam.setWmsTaskNo(mydoJob.getTaskNo()); + agvQueueParam.setPositionCode1(inSsjDevice.getDeviceCode()); + agvQueueParam.setStatus("0"); + List agvQueues = agvQueueService.selectAgvQueueList(agvQueueParam); + if (CollUtil.isEmpty(agvQueues)) { + //生成agv队列 + AgvQueue agvQueue = createAgvQueue(inSsjDevice, mydoJob); + //请求任务生成 + GenAgvSchedulingTaskResponse genAgvSchedulingTaskResponse = requestAgvQueue(mydoJob, agvQueue); + //修改wms任务状态 + if (genAgvSchedulingTaskResponse.getCode() == 0) { + //修改ctu执行状态 + agvQueue.setStatus("1"); + agvQueueService.updateAgvQueue(agvQueue); + log.info("向CTU下入库库作业"); + //修改mydoJob执行阶段 + mydoJob.setExecutionPhase("6"); + apiService.updateMydoJob(mydoJob); + } + } else { + AgvQueue agvQueue = agvQueues.get(0); + //请求任务生成 + GenAgvSchedulingTaskResponse genAgvSchedulingTaskResponse = requestAgvQueue(mydoJob, agvQueue); + //修改wms任务状态 + if (genAgvSchedulingTaskResponse.getCode() == 0) { + //修改ctu执行状态 + agvQueue.setStatus("1"); + agvQueueService.updateAgvQueue(agvQueue); + log.info("向CTU下入库库作业"); + //修改mydoJob执行阶段 + mydoJob.setExecutionPhase("6"); + apiService.updateMydoJob(mydoJob); + } + } + } else { + mydoJobParam.setEntranceId(inSsjDevice.getDeviceCode()); + mydoJobParam.setStatus("3"); + mydoJobParam.setTrayCode(inSsjDevice.getTrayCode()); + mydoJobs = apiService.getMydoJobList(mydoJobParam); + if (CollUtil.isNotEmpty(mydoJobs)) { + log.info("trayCode:{} 入库作业正在执行中", inSsjDevice.getTrayCode()); + } else { + log.info("trayCode:{} 无入库作业", inSsjDevice.getTrayCode()); + } + } + } + } + + /** + * 生成入库AGV队列 + * + * @param ssjDevice + * @param mydoJob + * @return + */ + private AgvQueue createAgvQueue(DeviceSsj ssjDevice, MydoJob mydoJob) { + //生成ctu执行队列 + AgvQueue agvQueue = new AgvQueue(); + String reqCode = IdUtil.fastSimpleUUID(); + agvQueue.setReqCode(reqCode); + agvQueue.setReqTime(DateUtil.formatDateTime(DateUtil.date())); + agvQueue.setAgvType("CTU"); + agvQueue.setPositionCode1(mydoJob.getCargoSpaceId()); + agvQueue.setPositionCode2(ssjDevice.getDeviceCode()); + agvQueue.setTaskType("F01"); + agvQueue.setCtnrType("0"); + agvQueue.setCtnrCode("0"); + agvQueue.setCtnrNum("0"); + agvQueue.setTaskMode("0"); + agvQueue.setPriority("10"); + agvQueue.setRequest("0"); + agvQueue.setWmsTraycode(mydoJob.getTrayCode()); + agvQueue.setWmsTaskNo(mydoJob.getTaskNo()); + agvQueue.setStatus("0"); + agvQueueService.insertAgvQueue(agvQueue); + return agvQueue; + } + + /** + * 请求海康生成agv任务,并修改wms任务状态 + * + * @param mydoJob + * @param agvQueue + */ + private GenAgvSchedulingTaskResponse requestAgvQueue(MydoJob mydoJob, AgvQueue agvQueue) { + //请求ctu + GenAgvSchedulingTaskRequest genAgvSchedulingTaskRequest = new GenAgvSchedulingTaskRequest(); + genAgvSchedulingTaskRequest.setReqCode(agvQueue.getReqCode()); + genAgvSchedulingTaskRequest.setTaskMode("0"); + genAgvSchedulingTaskRequest.setPriority("10"); + genAgvSchedulingTaskRequest.setTaskTyp("F01"); + genAgvSchedulingTaskRequest.setCtnrCode("0"); + genAgvSchedulingTaskRequest.setCtnrNum("0"); + genAgvSchedulingTaskRequest.setTaskMode("0"); + genAgvSchedulingTaskRequest.setTaskCode(mydoJob.getTaskNo()); + genAgvSchedulingTaskRequest.setGroupId(mydoJob.getTaskNo()); + genAgvSchedulingTaskRequest.setAgvTyp("CTU"); + genAgvSchedulingTaskRequest.setPositionSelStrategy("0"); + return apiService.genAgvSchedulingTask(genAgvSchedulingTaskRequest); + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/js/JsOutSsjThread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/js/JsOutSsjThread.java new file mode 100644 index 0000000..55f74a5 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/js/JsOutSsjThread.java @@ -0,0 +1,139 @@ +package com.ruoyi.schedule.scheduleTask.ssj.js; + +import cn.hutool.core.collection.CollUtil; +import com.ruoyi.api.domain.agv.genAgvSchedulingTask.GenAgvSchedulingTaskRequest; +import com.ruoyi.api.domain.agv.genAgvSchedulingTask.GenAgvSchedulingTaskResponse; +import com.ruoyi.api.domain.wcs2wms.MydoJob; +import com.ruoyi.api.service.IApiService; +import com.ruoyi.device.agv.domain.AgvQueue; +import com.ruoyi.device.agv.service.IAgvQueueService; +import com.ruoyi.device.ssj.domain.DeviceSsj; +import com.ruoyi.schedule.plcRead.ssj.PlcRead; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + + +@Slf4j +@Component +public class JsOutSsjThread { + + public HashMap> ssjCodeList; + + @Resource + private IApiService apiService; + @Resource + private IAgvQueueService agvQueueService; + + + @PostConstruct + public void init() { + ssjCodeList = new HashMap<>(); + ArrayList ssjCodes = new ArrayList<>(); + ssjCodes.add("G2001"); + ssjCodes.add("G3001"); + ssjCodes.add("G3010"); + ssjCodes.add("G4001"); + ssjCodes.add("G4010"); + ssjCodes.add("G5001"); + ssjCodes.add("G5010"); + ssjCodeList.put("ssjOut", ssjCodes); + } + + public void run() { + for (String type : ssjCodeList.keySet()) { + switch (type){ + case "ssjOut": + //正常输送线出口 + for (String ssjCode : ssjCodeList.get(type)) { + if (PlcRead.deviceMap.containsKey(ssjCode)) { + ssjOutDispatch(PlcRead.deviceMap.get(ssjCode)); + } else { + log.error("设备不存在 ssjCode{}", ssjCode); + } + } + break; + case "fluentOut": + //流利架出口 + break; + } + + } + } + + /** + * 输送线出口 + * @param outSsjDevice + */ + public void ssjOutDispatch(DeviceSsj outSsjDevice) { + //输送线出库 + if (outSsjDevice.getKx()==1){ + //查询对应的作业 + AgvQueue agvQueue = new AgvQueue(); + agvQueue.setPositionCode2(outSsjDevice.getDeviceCode()); + agvQueue.setStatus("0"); + List agvQueueList = agvQueueService.selectAgvQueueList(agvQueue); + if (CollUtil.isNotEmpty(agvQueueList)){ + for (AgvQueue queue : agvQueueList) { + MydoJob mydoJobParam = new MydoJob(); + mydoJobParam.setTaskNo(queue.getWmsTaskNo()); + mydoJobParam.setTrayCode(queue.getWmsTraycode()); + mydoJobParam.setCargoSpaceId(queue.getPositionCode1()); + List mydoJobList = apiService.getMydoJobList(mydoJobParam); + if (CollUtil.isNotEmpty(mydoJobList)){ + MydoJob mydoJob = mydoJobList.get(0); + requestAgvQueue(mydoJob, queue); + }else { + log.info("队列 agvCode {},无对应出库作业", agvQueue.getReqCode()); + } + } + }else { + log.info("ssjCode:{},没有待下发的出库任务", outSsjDevice.getDeviceCode()); + } + } + } + + /** + * 流利架出口 + * @param outSsjDevice + */ + public void fluentOutDispatch(DeviceSsj outSsjDevice) { + //查询流利架货位是否 + } + + /** + * 请求海康生成agv任务,并修改wms任务状态 + * @param mydoJob + * @param agvQueue + */ + private void requestAgvQueue(MydoJob mydoJob, AgvQueue agvQueue) { + //请求ctu + GenAgvSchedulingTaskRequest genAgvSchedulingTaskRequest = new GenAgvSchedulingTaskRequest(); + genAgvSchedulingTaskRequest.setReqCode(agvQueue.getReqCode()); + genAgvSchedulingTaskRequest.setTaskMode("0"); + genAgvSchedulingTaskRequest.setPriority("10"); + genAgvSchedulingTaskRequest.setTaskTyp("F01"); + genAgvSchedulingTaskRequest.setCtnrCode("0"); + genAgvSchedulingTaskRequest.setCtnrNum("0"); + genAgvSchedulingTaskRequest.setTaskMode("0"); + genAgvSchedulingTaskRequest.setTaskCode(mydoJob.getTaskNo()); + genAgvSchedulingTaskRequest.setGroupId(mydoJob.getTaskNo()); + genAgvSchedulingTaskRequest.setAgvTyp("CTU"); + genAgvSchedulingTaskRequest.setPositionSelStrategy("0"); + GenAgvSchedulingTaskResponse genAgvSchedulingTaskResponse = apiService.genAgvSchedulingTask(genAgvSchedulingTaskRequest); + if (genAgvSchedulingTaskResponse.getCode() == 0){ + //修改ctu执行状态 + agvQueue.setStatus("1"); + agvQueueService.updateAgvQueue(agvQueue); + log.info("向CTU下入库库作业"); + //修改mydoJob执行阶段 + mydoJob.setExecutionPhase("6"); + apiService.updateMydoJob(mydoJob); + } + } +} diff --git a/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/js/JsPickSsjTread.java b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/js/JsPickSsjTread.java new file mode 100644 index 0000000..8c059f0 --- /dev/null +++ b/ruoyi-admin/src/main/java/com/ruoyi/schedule/scheduleTask/ssj/js/JsPickSsjTread.java @@ -0,0 +1,87 @@ +package com.ruoyi.schedule.scheduleTask.ssj.js; + +import cn.hutool.core.collection.CollUtil; +import com.ruoyi.api.domain.wcs2wms.MydoJob; +import com.ruoyi.api.service.IApiService; +import com.ruoyi.common.core.text.Convert; +import com.ruoyi.device.agv.service.IAgvQueueService; +import com.ruoyi.device.ssj.domain.DeviceSsj; +import com.ruoyi.schedule.plcRead.ssj.PlcRead; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + + +@Slf4j +@Component +public class JsPickSsjTread { + + public HashMap> ssjCodeList; + + @Resource + private IApiService apiService; + @Resource + private IAgvQueueService agvQueueService; + + + @PostConstruct + public void init() { + ssjCodeList = new HashMap<>(); + ArrayList ssjCodes = new ArrayList<>(); + ssjCodes.add("G5013"); + ssjCodes.add("G5014"); + ssjCodes.add("G5015"); + ssjCodes.add("G5004"); + ssjCodes.add("G5005"); + ssjCodes.add("G5006"); + ssjCodes.add("G4013"); + ssjCodes.add("G4014"); + ssjCodes.add("G4015"); + ssjCodes.add("G4004"); + ssjCodes.add("G4005"); + ssjCodes.add("G4006"); + ssjCodeList.put("pick", ssjCodes); + } + + + @Scheduled(fixedDelay = 5000) + public void run(){ + for (String type : ssjCodeList.keySet()) { + if ("pick".equals(type)) { + for (String ssjCode : ssjCodeList.get(type)) { + if (PlcRead.deviceMap.containsKey(ssjCode)) { + pickDispatch(PlcRead.deviceMap.get(ssjCode)); + } else { + log.error("设备不存在 ssjCode{}", ssjCode); + } + } + } + } + } + + public void pickDispatch(DeviceSsj pickSsjDevice) { + if (pickSsjDevice.getDw() == 1 && pickSsjDevice.getTaskNo() > 0){ + Integer taskNo = pickSsjDevice.getTaskNo(); + MydoJob mydoJobParam = new MydoJob(); + mydoJobParam.setStatus("3"); + mydoJobParam.setTaskNo(Convert.toStr(taskNo)); + mydoJobParam.setExitId(pickSsjDevice.getDeviceCode()); + List mydoJobList = apiService.getMydoJobList(mydoJobParam); + if (CollUtil.isNotEmpty(mydoJobList)){ + MydoJob mydoJob = mydoJobList.get(0); + if ("4".equals(mydoJob.getExecutionPhase())){ + mydoJob.setExecutionPhase("5"); + apiService.updateMydoJob(mydoJob); + } + } + } + } + + +}