diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/acsEnum/StatusEnum.java b/wcs/nladmin-system/src/main/java/org/nl/acs/acsEnum/StatusEnum.java index e114d7fa..f9b46e0b 100644 --- a/wcs/nladmin-system/src/main/java/org/nl/acs/acsEnum/StatusEnum.java +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/acsEnum/StatusEnum.java @@ -55,25 +55,30 @@ public enum StatusEnum { QXXL_FULL_REQ("9", "取满框请求(清洗下料)", ""), QXXL_EMPTY_REQ("10", "叫空框请求(清洗下料)", ""), QXXL_COMPEL_REQ("11", "强制搬出请求(清洗下料)", ""), - KZSL_EMPTY_REQ("12","取空框请求(刻字上料)",""), + KZSL_EMPTY_REQ("12", "取空框请求(刻字上料)", ""), + EMPTY_REQ("22", "补空框请求(三工位)", ""), - HAIROU_IN_REQ("13","半成品入库(海柔)",""), - HAIROU_OUT_REQ("14","半成品出库(海柔)",""), - HAIROU_INVENTORY_REQ("15","盘点(海柔)",""), - HAIROU_EMPTY_OUT_REQ("16","空载具出库(海柔)",""), - HAIROU_EMPTY_IN_REQ("17","空载具入库(海柔)",""), + HAIROU_IN_REQ("13", "半成品入库(海柔)", ""), + HAIROU_OUT_REQ("14", "半成品出库(海柔)", ""), + HAIROU_INVENTORY_REQ("15", "盘点(海柔)", ""), + HAIROU_EMPTY_OUT_REQ("16", "空载具出库(海柔)", ""), + HAIROU_EMPTY_IN_REQ("17", "空载具入库(海柔)", ""), - HAIROU_LIKU_REQ("18","半成品库任务(海柔)",""), + TWO_TASK_1("10", "双点位任务", "取放/取取放放"), + TWO_TASK_2("12", "双点位任务", "取放取放"), + THREE_TASK("11", "三点位任务", "取倒放"), - HAIROU_RELOCATION("19","移库(海柔)",""), - HAIROU_CARRY("20","搬运(海柔)",""), - HAIROU_CHECK("21","盘库(海柔)",""), + HAIROU_LIKU_REQ("18", "半成品库任务(海柔)", ""), - TOTE_INBOUND("1","容器入库","tote_inbound"), - TOTE_OUTBOUND("2","容器出库","tote_outbound"), - TOTE_RELOCATION("3","容器移库","tote_relocation"), - TOTE_CARRY("4","容器搬运","tote_carry"), - TOTE_CHECK("5","容器盘库","tote_check"), + HAIROU_RELOCATION("19", "移库(海柔)", ""), + HAIROU_CARRY("20", "搬运(海柔)", ""), + HAIROU_CHECK("21", "盘库(海柔)", ""), + + TOTE_INBOUND("1", "容器入库", "tote_inbound"), + TOTE_OUTBOUND("2", "容器出库", "tote_outbound"), + TOTE_RELOCATION("3", "容器移库", "tote_relocation"), + TOTE_CARRY("4", "容器搬运", "tote_carry"), + TOTE_CHECK("5", "容器盘库", "tote_check"), //指令状态 INST_READY("0", "就绪", ""), @@ -86,9 +91,9 @@ public enum StatusEnum { TASK_RUNNING("1", "执行中", "6"), TASK_FINISH("2", "完成", "7"), TASK_CANCEL("3", "取消", "8"), - TASK_FULL_IN("4","满入",""), - TASK_EMPTY_OUT("5","空出",""), - TASK_CHECK_ANOMALY("6","盘点异常",""); + TASK_FULL_IN("4", "满入", ""), + TASK_EMPTY_OUT("5", "空出", ""), + TASK_CHECK_ANOMALY("6", "盘点异常", ""); private String code; private String desc; diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/agv/server/NDCAgvService.java b/wcs/nladmin-system/src/main/java/org/nl/acs/agv/server/NDCAgvService.java index e12db06c..d7a8459c 100644 --- a/wcs/nladmin-system/src/main/java/org/nl/acs/agv/server/NDCAgvService.java +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/agv/server/NDCAgvService.java @@ -73,4 +73,22 @@ public interface NDCAgvService { * @throws Exception */ public void sendAgvTwoInstToNDC(Instruction inst, Instruction inst2) throws Exception; + + /** + * 智能三线反馈诺宝AGV + * + * @param phase + * @param index + * @param result + * @return + */ + byte[] sendInstToNuoBaoNdc(int phase, int index, int result); + + /** + * 智能三线下发诺宝AGV指令 + * + * @param inst + * @param inst2 + */ + void sendNuoBaoAgvInst(Instruction inst, Instruction inst2); } diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/agv/server/dto/AgvDto.java b/wcs/nladmin-system/src/main/java/org/nl/acs/agv/server/dto/AgvDto.java index 74f3eb2a..1fc99b96 100644 --- a/wcs/nladmin-system/src/main/java/org/nl/acs/agv/server/dto/AgvDto.java +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/agv/server/dto/AgvDto.java @@ -1,12 +1,15 @@ package org.nl.acs.agv.server.dto; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; +import lombok.NoArgsConstructor; import java.io.Serializable; @Data @Builder +@AllArgsConstructor public class AgvDto implements Serializable { /** diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/agv/server/impl/NDCAgvServiceImpl.java b/wcs/nladmin-system/src/main/java/org/nl/acs/agv/server/impl/NDCAgvServiceImpl.java index 22c00340..85d7ee16 100644 --- a/wcs/nladmin-system/src/main/java/org/nl/acs/agv/server/impl/NDCAgvServiceImpl.java +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/agv/server/impl/NDCAgvServiceImpl.java @@ -5,9 +5,11 @@ import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.nl.acs.AcsConfig; +import org.nl.acs.acsEnum.StatusEnum; import org.nl.acs.agv.server.NDCAgvService; import org.nl.acs.agv.server.dto.AgvDto; import org.nl.acs.auto.run.NDCSocketConnectionAutoRun; +import org.nl.acs.auto.run.NdcNettyConnectionAutoRun; import org.nl.acs.auto.run.OneNDCSocketConnectionAutoRun; import org.nl.acs.device.service.DeviceService; import org.nl.acs.device.service.impl.DeviceServiceImpl; @@ -493,7 +495,7 @@ public class NDCAgvServiceImpl implements NDCAgvService { (byte) 0X00, (byte) 0X01, (byte) 0X00, (byte) 0X71, (byte) 0X00, (byte) 0X16, - (byte) 0X01, (byte) 0X80, + (byte) 0X01, (byte) prioritylow, (byte) 0X00, (byte) 0X01, (byte) ikeyhigh, (byte) ikeylow, (byte) ikeyhigh, (byte) ikeylow, @@ -512,4 +514,131 @@ public class NDCAgvServiceImpl implements NDCAgvService { System.out.println("下发agv指令数据:" + Bytes2HexString(b)); } + @Override + public byte[] sendInstToNuoBaoNdc(int phase, int index, int result) { + log.info("反馈agv动作数据--index:" + hexToString((byte) IntToHexHigh(index) & 0xFF) + hexToString((byte) IntToHexLow(index) & 0xFF) + ",phase:" + hexToString((byte) IntToHexHigh(phase) & 0xFF) + hexToString((byte) IntToHexLow(phase) & 0xFF)); + return (phase < 0 || index < 0) ? null : new byte[]{ + (byte) 0X87, (byte) 0XCD, + (byte) 0X00, (byte) 0X08, + (byte) 0X00, (byte) 0X0A, + (byte) 0X00, (byte) 0X01, + (byte) 0X00, (byte) 0X6D, + (byte) 0X00, (byte) 0X06, + (byte) IntToHexHigh(index), (byte) IntToHexLow(index), + (byte) 0X01, (byte) 0X17, + (byte) IntToHexHigh(phase), (byte) IntToHexLow(phase) + }; + } + + @Override + public void sendNuoBaoAgvInst(Instruction inst, Instruction inst2) { + int type = 1; + if (inst2 != null && StatusEnum.TWO_TASK_1.getCode().equals(inst.getInstruction_type())) { + type = 4; + } else if (inst2 != null && StatusEnum.TWO_TASK_2.getCode().equals(inst.getInstruction_type())) { + type = 3; + } else if (inst2 != null && StatusEnum.THREE_TASK.getCode().equals(inst.getInstruction_type())) { + type = 5; + } else if (inst2 == null && StatusEnum.THREE_TASK.getCode().equals(inst.getInstruction_type())) { + type = 2; + } + String instcode = inst.getLink_num(); + int priority = Integer.parseInt(inst.getPriority()) + 128; + DeviceService deviceService = SpringContextHolder.getBean(DeviceServiceImpl.class); + int startAddress1 = deviceService.queryAddressBydeviceCode(inst.getStart_point_code()); + int putAddress1 = deviceService.queryAddressBydeviceCode(inst.getPut_point_code()); + int nextAddress1 = deviceService.queryAddressBydeviceCode(inst.getNext_point_code()); + int startAddress2 = 0; + int putAddress2 = 0; + int nextAddress2 = 0; + if (ObjectUtil.isNotEmpty(inst2)) { + startAddress2 = deviceService.queryAddressBydeviceCode(inst2.getStart_point_code()); + putAddress2 = deviceService.queryAddressBydeviceCode(inst2.getPut_point_code()); + nextAddress2 = deviceService.queryAddressBydeviceCode(inst2.getNext_point_code()); + } + + + byte ikeyhigh = (byte) IntToHexHigh(Integer.parseInt(instcode)); + byte ikeylow = (byte) IntToHexLow(Integer.parseInt(instcode)); + byte typehigh = (byte) IntToHexHigh(type); + byte typelow = (byte) IntToHexLow(type); + + byte qhd1high = (byte) IntToHexHigh(startAddress1); + byte qhd1low = (byte) IntToHexLow(startAddress1); + + byte qhd2high = (type == 4 || type == 5) ? (byte) IntToHexHigh(startAddress2) : 0; + byte qhd2low = (type == 4 || type == 5) ? (byte) IntToHexLow(startAddress2) : 0; + + byte dld1high = (byte) IntToHexHigh(putAddress1); + byte dld1low = (byte) IntToHexLow(putAddress1); + + byte dld2high = (byte) IntToHexHigh(putAddress2); + byte dld2low = (byte) IntToHexLow(putAddress2); + + byte fhd1high = (byte) IntToHexHigh(nextAddress1); + byte fhd1low = (byte) IntToHexLow(nextAddress1); + + byte fhd2high = (byte) IntToHexHigh(nextAddress2); + byte fhd2low = (byte) IntToHexLow(nextAddress2); + + byte qhd3high = (type == 3 || type == 6) ? (byte) IntToHexHigh(startAddress1) : 0; + byte qhd3low = (type == 3 || type == 6) ? (byte) IntToHexLow(startAddress1) : 0; + + byte qhd4high = type == 6 ? (byte) IntToHexHigh(startAddress2) : 0; + byte qhd4low = type == 6 ? (byte) IntToHexLow(startAddress2) : 0; + + byte fhd3high = (type == 3 || type == 6) ? (byte) IntToHexHigh(nextAddress1) : 0; + byte fhd3low = (type == 3 || type == 6) ? (byte) IntToHexLow(nextAddress1) : 0; + + byte fhd4high = type == 6 ? (byte) IntToHexHigh(nextAddress2) : 0; + byte fhd4low = type == 6 ? (byte) IntToHexLow(nextAddress2) : 0; + + byte prioritylow = (byte) IntToHexLow(priority); + + String str = "十进制下发:"; + String str1 = "十六进制下发:"; + str1 += "ikey:" + hexToString(ikeyhigh & 0xFF) + hexToString(ikeylow & 0xFF); + + str += "/type:" + (type); + str1 += "/type:" + hexToString(typehigh & 0xFF) + hexToString(typelow & 0xFF); + + str1 += "/qhd1:" + hexToString(qhd1high & 0xFF) + hexToString(qhd1low & 0xFF); + str1 += "/qhd2:" + hexToString(qhd2high & 0xFF) + hexToString(qhd2low & 0xFF); + str1 += "/dld1:" + hexToString(dld1high & 0xFF) + hexToString(dld1low & 0xFF); + + str1 += "/dld2:" + hexToString(dld2high & 0xFF) + hexToString(dld2low & 0xFF); + str1 += "/fhd1:" + hexToString(fhd1high & 0xFF) + hexToString(fhd1low & 0xFF); + str1 += "/fhd2:" + hexToString(fhd2high & 0xFF) + hexToString(fhd2low & 0xFF); + + System.out.println(str); + System.out.println(str1); + + byte[] b = new byte[]{(byte) 0X87, (byte) 0XCD, + (byte) 0X00, (byte) 0X08, + (byte) 0X00, (byte) 0X22, + (byte) 0X00, (byte) 0X01, + (byte) 0X00, (byte) 0X71, + (byte) 0X00, (byte) 0X1E, + (byte) 0X01, (byte) prioritylow, + (byte) 0X00, (byte) 0X01, + ikeyhigh, ikeylow, + ikeyhigh, ikeylow, + typehigh, typelow, + qhd1high, qhd1low, + qhd2high, qhd2low, + dld1high, dld1low, + dld2high, dld2low, + fhd1high, fhd1low, + fhd2high, fhd2low, + qhd3high, qhd3low, + qhd4high, qhd4low, + fhd3high, fhd3low, + fhd4high, fhd4low, + }; + log.info("下发AGV作业指令--{}", str1); + + NdcNettyConnectionAutoRun.write(b); + System.out.println("下发agv指令数据:" + Bytes2HexString(b)); + } + } diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/auto/run/NdcNettyConnectionAutoRun.java b/wcs/nladmin-system/src/main/java/org/nl/acs/auto/run/NdcNettyConnectionAutoRun.java new file mode 100644 index 00000000..b2ecc646 --- /dev/null +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/auto/run/NdcNettyConnectionAutoRun.java @@ -0,0 +1,143 @@ +package org.nl.acs.auto.run; + +import com.alibaba.fastjson.JSON; +import io.netty.bootstrap.Bootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.*; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.nl.acs.AcsConfig; +import org.nl.acs.device_driver.basedriver.agv.nuoBaoNdc.NuoBaoNdcDeviceDriver; +import org.nl.acs.opc.Device; +import org.nl.acs.opc.DeviceAppService; +import org.nl.config.ProtocolCodec; +import org.nl.modules.common.exception.BadRequestException; +import org.nl.modules.system.service.ParamService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Arrays; + +import static org.nl.acs.agv.server.impl.NDCAgvServiceImpl.Bytes2HexString; + +/** + * @author onepiece + */ +@Slf4j +@Component +public class NdcNettyConnectionAutoRun extends AbstractAutoRunnable { + + @Autowired + private ParamService paramService; + @Autowired + private DeviceAppService deviceAppService; + + private static final int[] PHASES_CAR_ID = {0x67, 0x70, 0x71, 0x72, 0x73, 0x74}; + + private final Bootstrap bootstrap = new Bootstrap(); + private static final EventLoopGroup group = new NioEventLoopGroup(); + private static Channel channel; + + @Override + public String getCode() { + return NdcNettyConnectionAutoRun.class.getSimpleName(); + } + + @Override + public String getName() { + return "智能三线诺宝NDC连接"; + } + + @Override + public void autoRun() { + String ip = paramService.findByCode(AcsConfig.ONEAGVURL).getValue(); + int port = Integer.parseInt(paramService.findByCode(AcsConfig.ONEAGVPORT).getValue()); + try { + Bootstrap bootstrap = createBootstrap(); + ChannelFuture boot = bootstrap.connect(ip, port).sync(); + channel = boot.channel(); + channel.closeFuture().sync(); + } catch (Exception e) { + log.error("agv连接出现异常: {}", JSON.toJSONString(e)); + if (channel != null) { + channel.flush(); + channel.close(); + } + } finally { + group.shutdownGracefully(); + } + } + + private Bootstrap createBootstrap() { + return bootstrap.group(group) + .option(ChannelOption.SO_KEEPALIVE, true) + .option(ChannelOption.TCP_NODELAY, true) + .channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) { + ch.pipeline().addLast(new MsgHandler()); + } + }); + } + + @Override + public void stop() { + if (channel != null) { + channel.flush(); + channel.close(); + } + group.shutdownGracefully(); + } + + public static void write(byte[] b) { + try { + log.info("下发agv数据:" + Bytes2HexString(b)); + channel.writeAndFlush(Unpooled.copiedBuffer(b)); + } catch (Exception e) { + try { + log.info("再次下发agv数据:" + Bytes2HexString(b)); + channel.writeAndFlush(Unpooled.copiedBuffer(b)); + } catch (Exception e1) { + throw new BadRequestException(e1.getMessage()); + } + } + } + + class MsgHandler extends SimpleChannelInboundHandler { + @Override + @SneakyThrows + protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) { + if (msg.isReadable()) { + byte[] bytes = ProtocolCodec.readBody(msg); + if (bytes.length > 0) { + //执行阶段 + Integer phase = (Byte.toUnsignedInt(bytes[16]) << 8) + Byte.toUnsignedInt(bytes[17]); + //agv任务号 + Integer index = (Byte.toUnsignedInt(bytes[12]) << 8) + Byte.toUnsignedInt(bytes[13]); + //任务号 + Integer ikey = (Byte.toUnsignedInt(bytes[26]) << 8) + Byte.toUnsignedInt(bytes[27]); + //站点号 + Integer param = (Byte.toUnsignedInt(bytes[18]) << 8) + Byte.toUnsignedInt(bytes[19]); + //车号 + byte carId = bytes[20]; + Device device = null; + //Phase值为这些站点号param代表小车号,否则carId代表小车号 + if (Arrays.stream(PHASES_CAR_ID).anyMatch(v -> v == phase)) { + device = deviceAppService.findDeviceByCode(Integer.toString(param)); + } else { + device = deviceAppService.findDeviceByCode(Integer.toString(carId)); + } + if (device != null && device.getDeviceDriver() instanceof NuoBaoNdcDeviceDriver) { + NuoBaoNdcDeviceDriver deviceDriver = (NuoBaoNdcDeviceDriver) device.getDeviceDriver(); + deviceDriver.processSocket(bytes, phase, index, ikey, Byte.toUnsignedInt(carId), param); + } + } + } + } + } +} diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/device_driver/basedriver/agv/nuoBaoNdc/NuoBaoNdcDefination.java b/wcs/nladmin-system/src/main/java/org/nl/acs/device_driver/basedriver/agv/nuoBaoNdc/NuoBaoNdcDefination.java new file mode 100644 index 00000000..3c86c2fa --- /dev/null +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/device_driver/basedriver/agv/nuoBaoNdc/NuoBaoNdcDefination.java @@ -0,0 +1,49 @@ +package org.nl.acs.device_driver.basedriver.agv.nuoBaoNdc; + +import org.nl.acs.device_driver.DeviceDriver; +import org.nl.acs.device_driver.DeviceDriverDefination; +import org.nl.acs.opc.Device; +import org.nl.acs.opc.DeviceType; +import org.springframework.stereotype.Service; + +import java.util.LinkedList; +import java.util.List; + +/** + * 智能三线诺宝AGV + */ +@Service +public class NuoBaoNdcDefination implements DeviceDriverDefination { + @Override + public String getDriverCode() { + return "nuo_bao_ndc"; + } + + @Override + public String getDriverName() { + return "智能三线诺宝AGV"; + } + + @Override + public String getDriverDescription() { + return "智能三线诺宝AGV"; + } + + @Override + public DeviceDriver getDriverInstance(Device device) { + return (new NuoBaoNdcDeviceDriver()).setDevice(device).setDriverDefination(this); + + } + + @Override + public Class getDeviceDriverType() { + return NuoBaoNdcDeviceDriver.class; + } + + @Override + public List getFitDeviceTypes() { + List types = new LinkedList(); + types.add(DeviceType.agv); + return types; + } +} diff --git a/wcs/nladmin-system/src/main/java/org/nl/acs/device_driver/basedriver/agv/nuoBaoNdc/NuoBaoNdcDeviceDriver.java b/wcs/nladmin-system/src/main/java/org/nl/acs/device_driver/basedriver/agv/nuoBaoNdc/NuoBaoNdcDeviceDriver.java new file mode 100644 index 00000000..80e259cd --- /dev/null +++ b/wcs/nladmin-system/src/main/java/org/nl/acs/device_driver/basedriver/agv/nuoBaoNdc/NuoBaoNdcDeviceDriver.java @@ -0,0 +1,869 @@ +package org.nl.acs.device_driver.basedriver.agv.nuoBaoNdc; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.nl.acs.acsEnum.StatusEnum; +import org.nl.acs.agv.server.NDCAgvService; +import org.nl.acs.agv.server.dto.AgvDto; +import org.nl.acs.agv.server.impl.NDCAgvServiceImpl; +import org.nl.acs.auto.run.NdcNettyConnectionAutoRun; +import org.nl.acs.device.service.DeviceService; +import org.nl.acs.device_driver.DeviceDriver; +import org.nl.acs.device_driver.basedriver.hailiang_one.hailiang_cleaning_put_line.HailiangCleaningPutLineDeviceDriver; +import org.nl.acs.device_driver.basedriver.hailiang_one.hailiang_engraving_cache.HailiangEngravingCacheDeviceDriver; +import org.nl.acs.device_driver.basedriver.hailiang_one.hailiang_engraving_in.HailiangEngravingInDeviceDriver; +import org.nl.acs.device_driver.basedriver.hailiang_one.hailiang_engraving_machine.HailiangEngravingMachineDeviceDriver; +import org.nl.acs.device_driver.basedriver.hailiang_one.hailiang_packer_station.HailiangPackerStationDeviceDriver; +import org.nl.acs.device_driver.basedriver.standard_ordinary_site.StandardOrdinarySiteDeviceDriver; +import org.nl.acs.device_driver.basedriver.standard_storage.StandardStorageDeviceDriver; +import org.nl.acs.device_driver.driver.AbstractDeviceDriver; +import org.nl.acs.instruction.service.InstructionService; +import org.nl.acs.instruction.service.dto.Instruction; +import org.nl.acs.instruction.service.impl.InstructionServiceImpl; +import org.nl.acs.opc.Device; +import org.nl.acs.opc.DeviceAppService; +import org.nl.acs.task.service.TaskService; +import org.nl.acs.task.service.dto.TaskDto; +import org.nl.acs.task.service.impl.TaskServiceImpl; +import org.nl.modules.wql.util.SpringContextHolder; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +/** + * 智能三线诺宝AGV + * + * @author onepiece + */ +@Slf4j +@Data +@RequiredArgsConstructor +public class NuoBaoNdcDeviceDriver extends AbstractDeviceDriver implements DeviceDriver { + + private final InstructionService instructionService = SpringContextHolder.getBean(InstructionServiceImpl.class); + private final NDCAgvService ndcAgvService = SpringContextHolder.getBean(NDCAgvServiceImpl.class); + private final DeviceAppService deviceAppService = SpringContextHolder.getBean(DeviceAppService.class); + private final DeviceService deviceService = SpringContextHolder.getBean(DeviceService.class); + private final TaskService taskService = SpringContextHolder.getBean(TaskServiceImpl.class); + + /** + * 当前AGV正在执行的指令信息 + */ + private List instructions = null; + + /** + * AGV设备编码 + */ + private String device_code; + + /** + * AGV上报站点值 + */ + private int agv_address = 0; + private int agv_address_copy = 0; + + /** + * AGV上报当前phase值 + */ + private int currentPhase = 0; + + /** + * 存放AGV状态信息 + */ + private final AgvDto agvInfo = AgvDto.builder() + .name(this.getDevice().getDevice_code()) + .state("UNKNOWN") + .energyLevel("0") + .transportOrder(null) + .positionX("0") + .positionY("0") + .positionAngle("0") + .build(); + + /** + * 当AGV上报这些phase值时,param代表小车Id,此时carId无实际用途 + */ + private static final int[] PHASES_CAR_ID = {0x67, 0x70, 0x71, 0x72, 0x73, 0x74}; + + /** + * 取货点phase值 + */ + private static final int[] GET_PHASES = {0x03, 0x05, 0x07, 0x09, 0x1B, 0x1D, 0x1F, 0x21}; + + /** + * 倒料点phase值 + */ + private static final int[] FALL_PHASES = {0x0B, 0x0D, 0x0F, 0x11}; + + /** + * 放货点phase值 + */ + private static final int[] PUT_PHASES = {0x13, 0x15, 0x17, 0x19, 0x23, 0x25, 0x27, 0x29}; + + /** + * 开始任务 + */ + private static final int PHASE_01 = 0x01; + + /** + * 分配车ID + */ + private static final int PHASE_02 = 0x02; + + /** + * 到达取货点1 + */ + private static final int PHASE_03 = 0x03; + + /** + * 取货完毕 + */ + private static final int PHASE_05 = 0x05; + + /** + * 到达取货点2 + */ + private static final int PHASE_07 = 0x07; + + /** + * 取货完毕 + */ + private static final int PHASE_09 = 0x09; + + /** + * 到达倒料点1 + */ + private static final int PHASE_0B = 0x0B; + + /** + * 倒料完毕 + */ + private static final int PHASE_0D = 0x0D; + + /** + * 到达倒料点2 + */ + private static final int PHASE_0F = 0x0F; + + /** + * 倒料完毕 + */ + private static final int PHASE_11 = 0x11; + + /** + * 到达放货点1 + */ + private static final int PHASE_13 = 0x13; + + /** + * 放货完毕 + */ + private static final int PHASE_15 = 0x15; + + /** + * 到达放货点2 + */ + private static final int PHASE_17 = 0x17; + + /** + * 放货完毕 + */ + private static final int PHASE_19 = 0x19; + + /** + * 到达取货点3 + */ + private static final int PHASE_1B = 0x1B; + + /** + * 取货完毕 + */ + private static final int PHASE_1D = 0x1D; + + /** + * 到达取货点4 + */ + private static final int PHASE_1F = 0x1F; + + /** + * 取货完毕 + */ + private static final int PHASE_21 = 0x21; + + /** + * 到达放货点3 + */ + private static final int PHASE_23 = 0x23; + + /** + * 放货完毕 + */ + private static final int PHASE_25 = 0x25; + + /** + * 到达放货点4 + */ + private static final int PHASE_27 = 0x27; + + /** + * 放货完毕 + */ + private static final int PHASE_29 = 0x29; + + /** + * 任务完毕 + */ + private static final int PHASE_2A = 0x2A; + + /** + * 请求删除任务 + */ + private static final int PHASE_30 = 0x30; + + /** + * 任务删除确认 + */ + private static final int PHASE_FF = 0xFF; + + + /** + * 进入交通灯区域 + */ + private static final int PHASE_50 = 0x50; + + /** + * 离开交通灯区域 + */ + private static final int PHASE_51 = 0x51; + + + /** + * 到达位置点 + */ + private static final int PHASE_64 = 0x64; + + /** + * 称重就绪 + */ + private static final int PHASE_65 = 0x65; + + + /** + * 上报异常信息 + */ + private static final int PHASE_67 = 0x67; + + /** + * 上报X坐标 + */ + private static final int PHASE_70 = 0x70; + + /** + * 上报Y坐标 + */ + private static final int PHASE_71 = 0x71; + + /** + * 上报车辆角度 + */ + private static final int PHASE_72 = 0x72; + + /** + * 上报车辆电量 + */ + private static final int PHASE_73 = 0x73; + + /** + * 上报三色灯状态 + */ + private static final int PHASE_74 = 0x74; + + /** + * 处理NDC交互逻辑 + * + * @param arr NDC上报报文信息 + * @param phase phase值,AGV执行阶段 + * @param index NDC调度系统的任务号 + * @param ikey phase值不在phasesCarsId中,代表ACS指令号或关联编号,否则代表phase值对应的上报的信息异常、X坐标、Y坐标、角度、电量、三色灯状态等信息 + * @param carId phase值不在phasesCarsId中,代表小车Id,否则暂无实际作用 + * @param param phase值在phasesCarsId中,代表小车Id,否则代表站点号-即AGV地址 + */ + public synchronized void processSocket(byte[] arr, Integer phase, Integer index, Integer ikey, Integer carId, Integer param) { + this.device_code = this.getDevice_code(); + byte[] data = null; + this.setTaskCodeToAgvInfo(ikey, phase); + this.currentPhase = phase; + switch (phase) { + //开始任务 + case PHASE_01: + data = this.handleStartTask(ikey, index); + break; + //分配车Id + case PHASE_02: + this.assignCarId(ikey, carId); + break; + //到达取货点1、2、3、4 + case PHASE_03: + case PHASE_07: + case PHASE_1B: + case PHASE_1F: + data = this.toGetPoint(ikey, index); + break; + //取货完闭1、2、3、4 + case PHASE_05: + case PHASE_09: + case PHASE_1D: + case PHASE_21: + data = this.getFinish(ikey, index); + break; + //到达倒料点1、2 + case PHASE_0B: + case PHASE_0F: + data = this.toFallPoint(ikey, index); + break; + //倒料完毕1、2 + case PHASE_0D: + case PHASE_11: + data = this.fallFinish(ikey, index); + break; + //到达放货点1、2、3、4 + case PHASE_13: + case PHASE_17: + case PHASE_23: + case PHASE_27: + data = this.toPutPoint(ikey, index); + break; + //放货完毕1、2、3、4 + case PHASE_15: + case PHASE_19: + case PHASE_25: + case PHASE_29: + data = this.putFinish(ikey, index); + break; + //任务完成 + case PHASE_2A: + data = this.taskFinish(ikey, index); + break; + //删除任务 + case PHASE_30: + data = this.delTask(ikey, index); + break; + //删除任务确认 + case PHASE_FF: + data = this.delTaskConfirm(ikey, index); + break; + //进入交通灯区域 + case PHASE_50: + data = this.inLightArea(); + break; + //离开交通灯区域 + case PHASE_51: + data = this.outLightArea(); + break; + //到达位置点 + case PHASE_64: + data = this.arrivedPoint(param, index); + break; + //开始称重 + case PHASE_65: + data = this.startWeighing(phase, ikey, index, param); + break; + //上报异常信息 + case PHASE_67: + this.reportExceptionInfo(); + break; + //上报车辆X坐标 + case PHASE_70: + this.reportXCoordinate(ikey); + break; + //上报车辆Y坐标 + case PHASE_71: + this.reportYCoordinate(ikey); + break; + //上报车辆角度 + case PHASE_72: + this.reportAngle(ikey); + break; + //上报车辆电量 + case PHASE_73: + this.reportElectric(ikey); + break; + //上报三色灯状态 + case PHASE_74: + this.reportThreeColorStatus(ikey); + break; + default: + break; + } + if (data != null) { + NdcNettyConnectionAutoRun.write(data); + } + } + + + /** + * 开始任务 + * + * @param ikey 指令号或关联编号 + * @param index NDC的任务号 + */ + private byte[] handleStartTask(Integer ikey, Integer index) { + List instructions = this.getInstructions(ikey); + if (ObjectUtil.isNotEmpty(instructions)) { + boolean flag = false; + Instruction instruction = instructions.get(0); + TaskDto taskDto = taskService.findByTaskCode(instruction.getTask_code()); + if (taskDto != null) { + List taskDtos = taskService.findByLinkNumFromCache(taskDto.getLink_num()); + if (taskDtos != null) { + if (taskDtos.size() == 1) { + for (Instruction inst : instructions) { + inst.setInstruction_status(StatusEnum.INST_RUNNING.getCode()); + inst.setAgv_jobno(String.valueOf(index)); + inst.setSend_status(StatusEnum.YES.getCode()); + instructionService.update(inst); + log.info("修改指令执行信息成功,指令号:{}", inst.getInstruction_code()); + flag = true; + } + } else if (taskDtos.size() == 2) { + for (int i = 0; i < instructions.size(); i++) { + Instruction inst = instructions.get(i); + inst.setInstruction_status(StatusEnum.INST_RUNNING.getCode()); + inst.setAgv_jobno(String.valueOf(index)); + inst.setSend_status(StatusEnum.YES.getCode()); + instructionService.update(inst); + if (i == 1) { + flag = true; + } + log.info("修改指令执行信息成功,指令号:{}", inst.getInstruction_code()); + } + } + } + if (flag) { + return ndcAgvService.sendInstToNuoBaoNdc(currentPhase, index, 0); + } + } + } + return null; + } + + /** + * 上报车号 + * + * @param ikey 指令号或关联编号 + * @param carId 车辆Id + */ + private void assignCarId(Integer ikey, Integer carId) { + List instructions = this.getInstructions(ikey); + for (Instruction inst : instructions) { + inst.setCarno(String.valueOf(carId)); + instructionService.update(inst); + } + } + + /** + * 到达取货点1、2、3、4 + * + * @param ikey 指令号或关联编号 + * @param index AGV任务号 + */ + private byte[] toGetPoint(Integer ikey, Integer index) { + return processInstruction(ikey, index, currentPhase, new int[]{PHASE_03, PHASE_07, PHASE_1B, PHASE_1F}); + } + + /** + * 取货完成1、2、3、4 + * + * @param ikey 指令号或关联编号 + * @param index AGV任务号 + */ + private byte[] getFinish(Integer ikey, Integer index) { + return processInstruction(ikey, index, currentPhase, new int[]{PHASE_05, PHASE_09, PHASE_1D, PHASE_21}); + } + + /** + * 到达倒料点1、2 + * + * @param ikey + * @param index + * @return + */ + private byte[] fallFinish(Integer ikey, Integer index) { + return processInstruction(ikey, index, currentPhase, new int[]{PHASE_0D, PHASE_11}); + } + + /** + * 倒料完毕1、2 + * + * @param ikey + * @param index + * @return + */ + private byte[] toFallPoint(Integer ikey, Integer index) { + return processInstruction(ikey, index, currentPhase, new int[]{PHASE_0B, PHASE_0F}); + } + + /** + * 到达放货点1、2、3、4 + * + * @param ikey 指令号或关联编号 + * @param index AGV任务号 + */ + private byte[] toPutPoint(Integer ikey, Integer index) { + return processInstruction(ikey, index, currentPhase, new int[]{PHASE_13, PHASE_17, PHASE_23, PHASE_27}); + } + + /** + * 放货完毕1、2、3、4 + * + * @param ikey 指令号或关联编号 + * @param index AGV任务号 + */ + private byte[] putFinish(Integer ikey, Integer index) { + return processInstruction(ikey, index, currentPhase, new int[]{PHASE_15, PHASE_19, PHASE_25, PHASE_29}); + } + + /** + * 处理取、倒、放货逻辑 + * + * @param ikey + * @param index + * @param currentPhase + * @param phases + * @return + */ + private byte[] processInstruction(Integer ikey, Integer index, int currentPhase, int[] phases) { + Device device = this.getDeviceCodeByAgvAddress(agv_address); + if (device == null) { + log.info("AGV上报站点信息:{},根据站点信息查询设备为空!", agv_address); + return null; + } + List insts = this.getInstructions(ikey); + if (insts == null) { + log.info("AGV上报指令关联编号:{},根据指令关联编号查询指令信息为空!", ikey); + return null; + } + // 排序 + insts.sort(Comparator.comparing(Instruction::getCreate_time)); + Instruction inst = null; + // 根据当前阶段获取指令 + for (int i = 0; i < phases.length; i++) { + if (currentPhase == (phases[i])) { + inst = getInstructionAtIndex(insts, i); + break; + } + } + // 校验指令和设备起始点 + if (ObjectUtil.isEmpty(inst)) { + log.info("AGV上报phase值:{},获取指令信息为空!", currentPhase); + return null; + } + + boolean isValidPhase = false; + + //如果当前Phase值与取货点Phase值相同比较AGV上报站点与起点是否一致 + //如果当前Phase值与倒料点Phase值相同比较AGV上报站点与倒料点是否一致 + //如果当前Phase值与放货点Phase值相同比较AGV上报站点与放货点是否一致 + if (Arrays.asList(GET_PHASES).contains(currentPhase)) { + isValidPhase = StrUtil.equals(inst.getStart_device_code(), device.getDevice_code()); + } else if (Arrays.asList(FALL_PHASES).contains(currentPhase)) { + isValidPhase = StrUtil.equals(inst.getPut_device_code(), device.getDevice_code()); + } else if (Arrays.asList(PUT_PHASES).contains(currentPhase)) { + isValidPhase = StrUtil.equals(inst.getNext_device_code(), device.getDevice_code()); + } + + if (isValidPhase) { + this.setPhaseToDriver(device, currentPhase, index, inst); + if (device.getDeviceDriver() instanceof StandardStorageDeviceDriver) { + return ndcAgvService.sendInstToNuoBaoNdc(currentPhase, index, 0); + } + } else { + log.info("AGV上报站点:{},上报phase值:{},与查询到的指令信息:{},中的取、倒、放点位信息不一致!", agv_address, currentPhase, inst.getInstruction_code()); + } + return null; + } + + /** + * 获取指定索引处的指令,如果索引超出范围,返回 null + * + * @param insts + * @param index + * @return + */ + private Instruction getInstructionAtIndex(List insts, int index) { + if (index < 0 || index >= insts.size()) { + return null; + } + return insts.get(index); + } + + /** + * 任务完成 + * + * @param ikey 指令号或关联编号 + */ + private byte[] taskFinish(Integer ikey, Integer index) { + instructionService.finishByLinkNum(String.valueOf(ikey)); + this.instructions = null; + this.agvInfo.setTransportOrder(null); + return ndcAgvService.sendInstToNuoBaoNdc(currentPhase, index, 0); + } + + /** + * 删除任务 + * + * @param ikey 指令号或关联编号 + */ + private byte[] delTask(Integer ikey, Integer index) { + this.instructions = null; + this.agvInfo.setTransportOrder(null); + return ndcAgvService.sendInstToNuoBaoNdc(143, index, 0); + } + + /** + * 删除任务确认 + * + * @param ikey 指令号或关联编号 + */ + private byte[] delTaskConfirm(Integer ikey, Integer index) { + try { + instructionService.forceCancelByLinkNum(String.valueOf(ikey)); + this.instructions = null; + this.agvInfo.setTransportOrder(null); + } catch (Exception e) { + e.printStackTrace(); + } + return ndcAgvService.sendInstToNuoBaoNdc(currentPhase, index, 0); + } + + /** + * 到达位置点 + * + * @param param AGV站点 + */ + private byte[] arrivedPoint(Integer param, Integer index) { + agv_address = param; + agv_address_copy = agv_address; + return ndcAgvService.sendInstToNuoBaoNdc(currentPhase, index, 0); + } + + /** + * 开始称重 + * + * @param param AGV称重信息 + */ + private byte[] startWeighing(Integer phase, Integer ikey, Integer index, Integer param) { + if (agv_address < 1) { + log.info("AGV开始称重,上报站点信息有误:{}", agv_address); + return null; + } + agv_address = agv_address_copy; + String device_code = deviceService.queryDeviceCodeByAddress(agv_address); + if (ObjectUtil.isEmpty(device_code)) { + log.info("AGV上报重量时站点:" + agv_address + ",对应设备号为空!"); + return null; + } + //1、得到重量信息 + int weight = param * 10; + if (weight < 0) { + log.info("AGV上报重量小于0不反馈AGV信息,指令号:{}", ikey); + return null; + } + List insts = this.getInstructions(ikey); + if (insts == null) { + log.info("AGV上报重量时根据ikey:{},查询指令信息为空!", ikey); + return null; + } + insts.sort(Comparator.comparing(Instruction::getCreate_time)); + Instruction instruction = insts.stream() + .filter(inst -> StrUtil.isEmpty(inst.getWeight())) + .limit(1) + .findFirst() + .orElse(null); + if (instruction == null) { + log.info("未找到关联编号{}对应的指令", ikey); + return null; + } + if (StrUtil.equals(instruction.getStart_device_code(), device_code)) { + String now = DateUtil.now(); + instruction.setWeight(String.valueOf(weight)); + instruction.setUpdate_time(now); + instructionService.update(instruction); + TaskDto taskDto = taskService.findByCodeFromCache(instruction.getTask_code()); + if (taskDto != null) { + taskDto.setWeight(String.valueOf(weight)); + taskDto.setUpdate_time(now); + taskService.update(taskDto); + } + } + return ndcAgvService.sendAgvTwoModeInst(phase, index, 0); + } + + private void reportExceptionInfo() { + + } + + private byte[] inLightArea() { + return null; + } + + private byte[] outLightArea() { + return null; + } + + /** + * 上报x坐标 + * + * @param ikey x坐标 + */ + private void reportXCoordinate(Integer ikey) { + this.agvInfo.setPositionX(String.valueOf(ikey)); + } + + /** + * 上报y坐标 + * + * @param ikey y坐标 + */ + private void reportYCoordinate(Integer ikey) { + this.agvInfo.setPositionY(String.valueOf(ikey)); + } + + /** + * 上报AGV角度 + * + * @param ikey 角度 + */ + private void reportAngle(Integer ikey) { + this.agvInfo.setPositionAngle(String.valueOf(ikey)); + } + + /** + * 上报AGV电量 + * + * @param ikey 电量 + */ + private void reportElectric(Integer ikey) { + this.agvInfo.setEnergyLevel(String.valueOf(ikey)); + } + + /** + * 上报三色灯状态 + * + * @param ikey 三色灯状态 + */ + private void reportThreeColorStatus(Integer ikey) { + this.agvInfo.setState(""); + } + + /** + * 向AGV设置任务号 + * + * @param ikey 指令号或者关联编号 + * @param phase 执行阶段 + */ + private void setTaskCodeToAgvInfo(Integer ikey, Integer phase) { + if (ikey != 0 && ikey != null) { + if (!Arrays.asList(PHASES_CAR_ID).contains(phase)) { + if (this.instructions == null) { + this.instructions = this.getInstructions(ikey); + for (Instruction inst : this.instructions) { + this.agvInfo.setTransportOrder(inst.getTask_code()); + } + } + } + } + } + + /** + * 根据ikey查询指令信息 + * + * @param ikey 指令编号或者关联编号 + * @return + */ + private List getInstructions(Integer ikey) { + return instructionService.findByLinkNum(String.valueOf(ikey)); + } + + /** + * 抽取取放货公共逻辑 + * 根据AGV上报的地址值获取设备信息 + * + * @param agvAddress + * @return + */ + private Device getDeviceCodeByAgvAddress(Integer agvAddress) { + this.agv_address = this.agv_address == 0 ? this.agv_address_copy : this.agv_address; + if (agv_address < 1) { + //打日志 + return null; + } + String device_code = deviceService.queryDeviceCodeByAddress(agvAddress); + if (agvAddress != 0) { + String old_device_code = deviceService.queryDeviceCodeByAddress(agvAddress); + if (StrUtil.contains(old_device_code, "-")) { + String[] point = old_device_code.split("-"); + device_code = point[0]; + } else if (StrUtil.contains(old_device_code, ".")) { + String[] point = old_device_code.split("\\."); + device_code = point[0]; + } else { + device_code = old_device_code; + } + } + return deviceAppService.findDeviceByCode(device_code); + } + + private void setPhaseToDriver(Device device, Integer phase, Integer index, Instruction inst) { + //刻字缓存位 + HailiangEngravingCacheDeviceDriver hailiangEngravingCacheDeviceDriver; + //刻字机工位 + HailiangEngravingMachineDeviceDriver hailiangEngravingMachineDeviceDriver; + //包装机工位 + HailiangPackerStationDeviceDriver hailiangPackerStationDeviceDriver; + //普通站点 + StandardOrdinarySiteDeviceDriver standardOrdinarySiteDeviceDriver; + //货架 + StandardStorageDeviceDriver standardStorageDeviceDriver; + //刻字上料线体 + HailiangEngravingInDeviceDriver hailiangEngravingInDeviceDriver; + // + HailiangCleaningPutLineDeviceDriver hailiangCleaningPutLineDeviceDriver; + if (device.getDeviceDriver() instanceof HailiangEngravingMachineDeviceDriver) { + hailiangEngravingMachineDeviceDriver = (HailiangEngravingMachineDeviceDriver) device.getDeviceDriver(); + hailiangEngravingMachineDeviceDriver.set(phase, index, inst); + } + + if (device.getDeviceDriver() instanceof HailiangEngravingCacheDeviceDriver) { + hailiangEngravingCacheDeviceDriver = (HailiangEngravingCacheDeviceDriver) device.getDeviceDriver(); + hailiangEngravingCacheDeviceDriver.set(phase, index, inst); + } + + if (device.getDeviceDriver() instanceof StandardOrdinarySiteDeviceDriver) { + standardOrdinarySiteDeviceDriver = (StandardOrdinarySiteDeviceDriver) device.getDeviceDriver(); + standardOrdinarySiteDeviceDriver.set(phase, index, inst); + } + + if (device.getDeviceDriver() instanceof HailiangPackerStationDeviceDriver) { + hailiangPackerStationDeviceDriver = (HailiangPackerStationDeviceDriver) device.getDeviceDriver(); + hailiangPackerStationDeviceDriver.set(phase, index, inst); + } + + if (device.getDeviceDriver() instanceof HailiangEngravingInDeviceDriver) { + hailiangEngravingInDeviceDriver = (HailiangEngravingInDeviceDriver) device.getDeviceDriver(); + hailiangEngravingInDeviceDriver.set(phase, index, inst); + } + + if (device.getDeviceDriver() instanceof HailiangCleaningPutLineDeviceDriver) { + hailiangCleaningPutLineDeviceDriver = (HailiangCleaningPutLineDeviceDriver) device.getDeviceDriver(); + hailiangCleaningPutLineDeviceDriver.set(phase, index, inst); + } + } + + +}