From d5671fa10553b94904e5bea18e86712e044ac9d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?LAPTOP-S9HJSOEB=5C=E6=98=8A=E5=A4=A9?= Date: Mon, 30 Oct 2023 16:18:17 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=8E=E8=A7=86=E8=A7=89?= =?UTF-8?q?=E6=8E=A7=E5=88=B6=E7=81=B5=E9=97=AA=E7=9A=84=E7=9B=B8=E5=85=B3?= =?UTF-8?q?tcp=E8=BF=9E=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/config/ConfigProperties.java | 6 + .../web/service/CategoryService.java | 1 + .../web/service/EmptyCheckService.java | 384 ++++++++++++++++++ .../zhehekeji/web/service/InitService.java | 6 + .../web/service/client/ClientChanel.java | 102 +++++ .../web/service/client/ClientCodeMap.java | 24 ++ .../zhehekeji/web/service/client/Decoder.java | 104 +++++ .../zhehekeji/web/service/client/Encoder.java | 24 ++ .../web/service/client/MessageConverter.java | 13 + .../service/client/NettyConnectHandler.java | 44 ++ .../web/service/client/NettyServer.java | 63 +++ .../web/service/client/Transmission.java | 48 +++ web/src/main/resources/application-prod.yml | 5 +- 13 files changed, 822 insertions(+), 2 deletions(-) create mode 100644 web/src/main/java/com/zhehekeji/web/service/EmptyCheckService.java create mode 100644 web/src/main/java/com/zhehekeji/web/service/client/ClientChanel.java create mode 100644 web/src/main/java/com/zhehekeji/web/service/client/ClientCodeMap.java create mode 100644 web/src/main/java/com/zhehekeji/web/service/client/Decoder.java create mode 100644 web/src/main/java/com/zhehekeji/web/service/client/Encoder.java create mode 100644 web/src/main/java/com/zhehekeji/web/service/client/MessageConverter.java create mode 100644 web/src/main/java/com/zhehekeji/web/service/client/NettyConnectHandler.java create mode 100644 web/src/main/java/com/zhehekeji/web/service/client/NettyServer.java create mode 100644 web/src/main/java/com/zhehekeji/web/service/client/Transmission.java diff --git a/web/src/main/java/com/zhehekeji/web/config/ConfigProperties.java b/web/src/main/java/com/zhehekeji/web/config/ConfigProperties.java index 2c919dd..33e2394 100644 --- a/web/src/main/java/com/zhehekeji/web/config/ConfigProperties.java +++ b/web/src/main/java/com/zhehekeji/web/config/ConfigProperties.java @@ -38,6 +38,12 @@ public class ConfigProperties { private ScanCodeMode scanCodeMode; + /** + * TCP服务端端口 + */ + private Integer serverPort; + + @Data public static class CameraConfig{ diff --git a/web/src/main/java/com/zhehekeji/web/service/CategoryService.java b/web/src/main/java/com/zhehekeji/web/service/CategoryService.java index 4c3b61b..b4284b5 100644 --- a/web/src/main/java/com/zhehekeji/web/service/CategoryService.java +++ b/web/src/main/java/com/zhehekeji/web/service/CategoryService.java @@ -55,6 +55,7 @@ public class CategoryService { public List list(String name){ List list = categoryMapper.selectList(new QueryWrapper().like("name",name)); + return list; } diff --git a/web/src/main/java/com/zhehekeji/web/service/EmptyCheckService.java b/web/src/main/java/com/zhehekeji/web/service/EmptyCheckService.java new file mode 100644 index 0000000..a679546 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/EmptyCheckService.java @@ -0,0 +1,384 @@ +package com.zhehekeji.web.service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.zhehekeji.web.entity.Stock; +import com.zhehekeji.web.entity.Street; +import com.zhehekeji.web.mapper.StockMapper; +import com.zhehekeji.web.pojo.stock.CheckStatus; +import com.zhehekeji.web.pojo.stock.RowColumnStatus; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.*; + +@Service +@Slf4j +public class EmptyCheckService { +// +// @Resource +// private StreetService streetService; +// @Resource +// private StockMapper stockMapper; +// @Resource +// private CheckSummaryMapper checkSummaryMapper; +// @Resource +// private CheckStreetSummaryMapper checkStreetSummaryMapper; +// @Resource +// private EmptyCheckMapper emptyCheckMapper; +// +// public PageInfo checkSummaryPageInfo(EmptyCheckSearch emptyCheckSearch){ +// PageHelper.startPage(emptyCheckSearch.getPageNum(),emptyCheckSearch.getPageSize()); +// QueryWrapper queryWrapper = new QueryWrapper<>(); +// if(!StringUtils.isEmpty(emptyCheckSearch.getTaskId())){ +// queryWrapper.eq("task_id",emptyCheckSearch.getTaskId()); +// } +// if(emptyCheckSearch.getStartTime() != null ){ +// queryWrapper.gt("start_time",emptyCheckSearch.getStartTime()); +// } +// if(emptyCheckSearch.getEndTime() != null){ +// queryWrapper.lt("start_time",emptyCheckSearch.getEndTime()); +// } +// List checkSummaries = checkSummaryMapper.selectList(queryWrapper); +// return new PageInfo<>(checkSummaries); +// } +// +// public List checkStreetSummaries(String taskId){ +// return checkStreetSummaryMapper.list(taskId); +// } +// +// /** +// * 盘点统计 +// */ +// public void CheckSummary(String SRMNumber,String taskId){ +// Street street = streetService.getStreetByPlcId(SRMNumber); +// if(street == null){ +// log.error(" no SRMNumer:{}",SRMNumber); +// return; +// } +// List stockList = stockMapper.selectList(new QueryWrapper().eq("street_id",street.getId())); +// /** +// * 空货位的数量 +// */ +// Integer emptyCount = 0; +// +// /** +// * 空托盘的数量(只有托盘没有货物) +// */ +// Integer emptyGoodsCount = 0; +// +// /** +// * 有货物的数量 +// */ +// Integer goodsCount = 0; +// for(Stock stock: stockList){ +// if(stock.getEmptyStatus() == 3){ +// //没托盘 +// emptyCount++; +// }else if(stock.getEmptyStatus() == 2){ +// //有货物 +// goodsCount++; +// }else if(stock.getEmptyStatus() == 1){ +// //有托盘 没货物 +// emptyGoodsCount++; +// } +// } +// //更新盘点统计 +// List checkStreetSummaries = checkStreetSummaryMapper.selectList(new QueryWrapper().eq("street_id",street.getId()).eq("lotnum",taskId)); +// if(checkStreetSummaries.size() > 0){ +// CheckStreetSummary checkStreetSummary = checkStreetSummaries.get(0); +// checkStreetSummary.setStreetId(street.getId()); +// checkStreetSummary.setEmptyCount(emptyCount); +// checkStreetSummary.setEmptyGoodsCount(emptyGoodsCount); +// checkStreetSummary.setGoodsCount(goodsCount); +// checkStreetSummary.setEndTime(LocalDateTime.now()); +// checkStreetSummaryMapper.updateById(checkStreetSummary); +// } +// +// } +// +// /** +// * 空托盘检测 按行开始 +// * @param +// */ +// public void emptyCheckStart(String SRMNUmber,String taskId){ +// log.info("空托盘检测 按行开始,SRMNUmber:{},taskNo:{}",SRMNUmber,taskId); +// Street street = streetService.getStreetByPlcId(SRMNUmber); +// if(street == null){ +// log.error(" no SRMNumer:{}",SRMNUmber); +// return; +// } +// CheckSummary checkSummary = checkSummaryMapper.selectById(taskId); +// if(checkSummary == null){ +// checkSummary = new CheckSummary(); +// checkSummary.setTaskId(taskId); +// checkSummary.setStartTime(LocalDateTime.now()); +// checkSummaryMapper.insert(checkSummary); +// } +// Map map = new HashMap<>(); +// //将这些位置的旧数据清空 +// UpdateWrapper wrapper = new UpdateWrapper<>(); +//// wrapper.eq("street_id",street.getId()).eq("`row`",row).ge("`column`",startColumn).le("`column`",endColumn); +// emptyCheckMapper.delete(wrapper); +// //新增空的盘点统计 +// List checkStreetSummaries = checkStreetSummaryMapper.selectList(new QueryWrapper().eq("street_id",street.getId()).eq("task_id",taskId)); +// if(checkStreetSummaries.size() == 0){ +// log.info("新增盘点统计,SRMNUmber:{},taskNo:{}",SRMNUmber,taskId); +// CheckStreetSummary checkStreetSummary = new CheckStreetSummary(); +// checkStreetSummary.setTaskId(taskId); +// checkStreetSummary.setStartTime(LocalDateTime.now()); +// checkStreetSummary.setStreetId(street.getId()); +// checkStreetSummary.setEmptyCount(0); +// checkStreetSummary.setGoodsCount(0); +// checkStreetSummary.setEmptyGoodsCount(0); +// checkStreetSummaryMapper.insert(checkStreetSummary); +// } +// +// } +// +// public void updateEmptyCheckLastTime(String taskId,String SRMNumber){ +// CheckSummary checkSummary = new CheckSummary(); +// checkSummary.setTaskId(taskId); +// checkSummary.setEndTime(LocalDateTime.now()); +// checkSummaryMapper.updateById(checkSummary); +// Street street = streetService.getStreetByPlcId(SRMNumber); +// +// if(street != null){ +// CheckStreetSummary checkStreetSummary = checkStreetSummaryMapper.get(street.getId(),taskId); +// Integer count = emptyCheckMapper.selectCount(new QueryWrapper().eq("task_id",taskId).eq("street_id",street.getId()).eq("empty_status",1)); +// checkStreetSummary.setEmptyCount(count); +// checkStreetSummary.setEndTime(LocalDateTime.now()); +// checkStreetSummaryMapper.updateEmptyCount(street.getId(),taskId,count); +// } +// } +// +// public void updateCheckLastTime(String taskId,String SRMNumber,String goodsCode){ +// CheckSummary checkSummary = new CheckSummary(); +// checkSummary.setTaskId(taskId); +// checkSummary.setEndTime(LocalDateTime.now()); +// checkSummaryMapper.updateById(checkSummary); +// Street street = streetService.getStreetByPlcId(SRMNumber); +// +// if(street != null){ +// CheckStreetSummary checkStreetSummary = checkStreetSummaryMapper.get(street.getId(),taskId); +// //托盤組 11111111 +// int c = checkStreetSummary.getGoodsCount() + 1; +// checkStreetSummaryMapper.updateGoodsCount(street.getId(),taskId,c); +// } +// } +// +// public String getEmptyStatus(Integer streetId,Integer side,Integer leftRight,Integer row,Integer startColumn,Integer endColumn){ +// +// List emptyChecks = emptyCheckMapper.selectList(new QueryWrapper().eq("`row`",row).eq("street_id",streetId).eq("direction",leftRight).eq("side",side).ge("`column`",startColumn).le("`column`",endColumn)); +// Map stockMap = new HashMap<>(emptyChecks.size()*2); +// emptyChecks.forEach(stock -> { +// stockMap.put(stock.getColumn(),stock); +// }); +// //int columns = side == 1?street.getLeftColumn():street.getRightColumn(); +// StringBuffer sb = new StringBuffer(); +// for(int i = startColumn;i<=endColumn;i++){ +// if(stockMap.get(i) != null && stockMap.get(i).getEmptyStatus() == 2){ +// sb.append(1); +// }else { +// sb.append(0); +// } +// } +// log.info("【空货位盘点结果】streetId:{},leftright:{},side:{},row:{},startColumn:{},endColumn:{},emptyStatus:{}",streetId,leftRight,side,row,startColumn,endColumn,sb); +// return sb.toString(); +// +// } +// +// public Map getAllEmptyStatus(String SRMNUmber,Integer row,Integer startColumn,Integer endColumn){ +// Street street = streetService.getStreetByPlcId(SRMNUmber); +// if(street == null){ +// return null; +// } +// Map map = new HashMap<>(); +// //判断单双货架 +// String L01 = getEmptyStatus(street.getId(),1,1,row,startColumn,endColumn); +// map.put("L01",L01); +// String R01 = getEmptyStatus(street.getId(),1,2,row,startColumn,endColumn); +// map.put("R01",R01); +// if(street.getLeftType() == 1){ +// //双伸的 +// String L02 = getEmptyStatus(street.getId(),2,1,row,startColumn,endColumn); +// map.put("L02",L02); +// } +// if(street.getRightType() == 1){ +// String R02 = getEmptyStatus(street.getId(),2,2,row,startColumn,endColumn); +// map.put("R02",R02); +// } +// return map; +// } +// +// public void emptyCheckLog(ECTransmission ecTransmission){ +// Street street = streetService.getStreetByPlcId(ecTransmission.getSRMNumber()); +// if(street == null){ +// log.error(" no SRMNumer:{}",ecTransmission.getSRMNumber()); +// return; +// } +// EmptyCheck emptyCheck = emptyCheckMapper.getOne(street.getId(),ecTransmission.getDirection(),ecTransmission.getSide(),ecTransmission.getRow(),ecTransmission.getColumn()); +// +// if(emptyCheck == null){ +// emptyCheck = new EmptyCheck(); +// emptyCheck.setStreetId(street.getId()); +// emptyCheck.setOrderNum(ecTransmission.getTaskId()); +// emptyCheck.setSide(ecTransmission.getSide()); +// emptyCheck.setDirection(ecTransmission.getDirection()); +// //0:未盘点 1:有货 2:无货 +// if(ecTransmission.getIsEmpty().equals("0")){ +// emptyCheck.setEmptyStatus(2); +// }else { +// emptyCheck.setEmptyStatus(1); +// } +// emptyCheck.setRow(ecTransmission.getRow()); +// emptyCheck.setColumn(ecTransmission.getColumn()); +// }else { +// emptyCheck.setOrderNum(ecTransmission.getTaskId()); +// if(ecTransmission.getIsEmpty().equals("0")){ +// log.info("goodsLocation:{}, status: {}", ecTransmission.getGoodsLocation(), ecTransmission.getIsEmpty()); +// emptyCheck.setEmptyStatus(2); +// }else { +// log.info("goodsLocation:{}, status: {}", ecTransmission.getGoodsLocation(), ecTransmission.getIsEmpty()); +// emptyCheck.setEmptyStatus(1); +// } +// } +// emptyCheckMapper.insertOrUpdate(emptyCheck); +// +// } +// +// public CheckStatus emptyStatusByRowColumn(RowColumnStatus rowColumnStatus){ +// CheckStatus checkStatus = new CheckStatus(); +// checkStatus.setColumnStart(rowColumnStatus.getColumnStart()); +// checkStatus.setColumnEnd(rowColumnStatus.getColumnEnd()); +// checkStatus.setRowStart(rowColumnStatus.getRowStart()); +// checkStatus.setRowEnd(rowColumnStatus.getRowEnd()); +// +// ListstockList = new ArrayList<>(); +// //默认全部盘点正确 +// Map rowTabStatus = new LinkedHashMap<>(); +// int columns = 0; +// int rows = 0; +// for(String rowTab: rowColumnStatus.getRowTabs()){ +// rows = getMax(rowTab,rows); +// rowTabStatus.put(rowTab,0); +// } +// Map columnTabStatus = new LinkedHashMap<>(); +// for(String column:rowColumnStatus.getColumnTabs()){ +// columns = getMax(column,columns); +// columnTabStatus.put(column,0); +// } +// +// List stocks = emptyCheckMapper.selectList(new QueryWrapper().select("empty_status","`row`","`column`").eq("`street_id`",rowColumnStatus.getStreetId()).eq("direction",rowColumnStatus.getDirection()).eq("side",rowColumnStatus.getSide())); +// if(CollectionUtils.isEmpty(stocks)){ +// stockInit(rowColumnStatus.getRowStart(), rowColumnStatus.getRowEnd(), rowColumnStatus.getColumnStart(), rowColumnStatus.getColumnEnd(), null,stocks); +// checkStatus.setEmptyStatus(stocks); +// checkStatus.setRowTabStatus(rowTabStatus); +// checkStatus.setColumnTabStatus(columnTabStatus); +// return checkStatus; +// } +// for(EmptyCheck emptyCheck: stocks){ +// if(emptyCheck.getEmptyStatus() == null){ +// continue; +// } +// if(emptyCheck.getColumn()>= rowColumnStatus.getColumnStart() && emptyCheck.getColumn()<= rowColumnStatus.getColumnEnd() && emptyCheck.getRow() >= rowColumnStatus.getRowStart() && emptyCheck.getRow()<= rowColumnStatus.getRowEnd()){ +// stockList.add(emptyCheck); +// } +// if(!CollectionUtils.isEmpty(rowColumnStatus.getRowTabs())){ +// for(String rowTab: rowColumnStatus.getRowTabs()){ +// if(isRowColumnInTab(emptyCheck,rowTab,true)){ +// tabStatus(emptyCheck,rowTabStatus,rowTab); +// } +// } +// } +// if(!CollectionUtils.isEmpty(rowColumnStatus.getColumnTabs())){ +// for(String columnTab: rowColumnStatus.getColumnTabs()){ +// if(isRowColumnInTab(emptyCheck,columnTab,false)){ +// tabStatus(emptyCheck,columnTabStatus,columnTab); +// } +// } +// } +// } +// +// checkStatus.setColumnTabStatus(columnTabStatus); +// checkStatus.setRowTabStatus(rowTabStatus); +// checkStatus.setEmptyStatus(stockInit(rowColumnStatus.getRowStart(), rowColumnStatus.getRowEnd(), rowColumnStatus.getColumnStart(), rowColumnStatus.getColumnEnd(), null,stockList)); +// return checkStatus; +// } +// +// private List stockInit(Integer rowStart, Integer rowEnd,Integer columnStart,Integer columnEnd,String shelveId,List readyList){ +// Map> map = new HashMap<>(); +// readyList.forEach(stock -> { +// if(map.get(stock.getRow()) == null){ +// Map columnMap = new HashMap<>(); +// columnMap.put(stock.getColumn(),true); +// map.put(stock.getRow(),columnMap); +// }else { +// Map columnMap = map.get(stock.getRow()); +// if(columnMap.get(stock.getColumn()) == null){ +// columnMap.put(stock.getColumn(),true); +// } +// map.put(stock.getRow(),columnMap); +// } +// +// }); +// for(int i = rowStart;i<=rowEnd;i++){ +// Map columnMap = map.get(i); +// for(int j = columnStart;j<=columnEnd;j++){ +// if(columnMap == null || columnMap.get(j) == null){ +// EmptyCheck emptyCheck = new EmptyCheck(); +// emptyCheck.setColumn(j); +// emptyCheck.setRow(i); +// emptyCheck.setEmptyStatus(0); +// //stock.setShelveId(shelveId); +// readyList.add(emptyCheck); +// } +// } +// } +// return readyList; +// +// } +// +// //0:未知 1:空 2:非空 +// //未知 > 空 > 非空 +// private void tabStatus(EmptyCheck stock,Map tabStatus,String tab){ +// if(tabStatus.get(tab) == null){ +// tabStatus.put(tab,stock.getEmptyStatus()); +// }else { +// Integer status = tabStatus.get(tab); +// if(stock.getEmptyStatus() == 0){ +// //未知为最优先状态 +// tabStatus.put(tab,0); +// }else if(stock.getEmptyStatus() == 1 && status != 0) { +// tabStatus.put(tab, 1); +// } +// } +// } +// +// private Boolean isRowColumnInTab(EmptyCheck stock,String tab,Boolean isRow){ +// String [] strings = tab.split(" - "); +// Integer tabStart = Integer.valueOf(strings[0]); +// Integer tabEnd = Integer.valueOf(strings[1]); +// if(isRow){ +// return stock.getRow() >= tabStart && stock.getRow() <= tabEnd; +// }else { +// return stock.getColumn() >= tabStart && stock.getColumn() <= tabEnd; +// } +// } +// +// public Integer getMax(String tab,Integer max){ +// String [] strings = tab.split(" - "); +// Integer tabStart = Integer.valueOf(strings[0]); +// Integer tabEnd = Integer.valueOf(strings[1]); +// int a = tabStart > tabEnd ? tabStart : tabEnd; +// a = max > a ? max : a; +// return a; +// } + +} diff --git a/web/src/main/java/com/zhehekeji/web/service/InitService.java b/web/src/main/java/com/zhehekeji/web/service/InitService.java index 31a61d3..8a00dd8 100644 --- a/web/src/main/java/com/zhehekeji/web/service/InitService.java +++ b/web/src/main/java/com/zhehekeji/web/service/InitService.java @@ -12,6 +12,7 @@ import com.zhehekeji.web.lib.joyware.NetSDKLib; import com.zhehekeji.web.mapper.CameraMapper; import com.zhehekeji.web.mapper.SensorGunMapper; import com.zhehekeji.web.mapper.StreetMapper; +import com.zhehekeji.web.service.client.NettyServer; import com.zhehekeji.web.service.ksec.KsecNettyClient; import com.zhehekeji.web.service.robotic.NettyClient; import com.zhehekeji.web.service.sick.SickNettyClient; @@ -51,6 +52,9 @@ public class InitService implements ApplicationRunner { @Resource private CameraService cameraService; + @Resource + private NettyServer nettyServer; + @Bean public CameraControlLoginModule cameraControlLoginModule(){ CameraControlLoginModule cameraControlLoginModule = null; @@ -78,6 +82,8 @@ public class InitService implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { + + nettyServer.CreateNettyServer(configProperties.getServerPort()); //球机登录 List cameras = cameraMapper.selectByMap(new HashMap<>(0)); cameras.forEach(camera -> { diff --git a/web/src/main/java/com/zhehekeji/web/service/client/ClientChanel.java b/web/src/main/java/com/zhehekeji/web/service/client/ClientChanel.java new file mode 100644 index 0000000..ea0984a --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/client/ClientChanel.java @@ -0,0 +1,102 @@ +package com.zhehekeji.web.service.client; + + +import io.netty.channel.Channel; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetSocketAddress; +import java.time.LocalDateTime; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 所有的客户端的chanel + */ +@Slf4j +public class ClientChanel { + + + static final Logger tcpLogger = LoggerFactory.getLogger("tcp"); + + /** + * key : 巷道标识符 + */ + static Map channelMap = new ConcurrentHashMap<>(); + + + /** + * key : 巷道标识符 + */ + static Map channelStringTime = new ConcurrentHashMap<>(); + + + /** + * key :IP + * value: 巷道标识符 + */ + static Map IP_SRMNumberMap = new ConcurrentHashMap<>(); + + /** + * key :巷道标识符 + * value: IP + */ + static Map SRMNumber_IPMap = new ConcurrentHashMap<>(); + + public static void putIp(String ip, String ID) { + IP_SRMNumberMap.put(ip, ID); + } + + public static void putSRMNUmber_Ip(String ID, String ip) { + SRMNumber_IPMap.put(ID, ip); + IP_SRMNumberMap.put(ip, ID); + } + + public static String getIpFromId(String ID) { + return SRMNumber_IPMap.get(ID); + } + + public static void deleteIp(String ip) { + IP_SRMNumberMap.remove(ip); + } + + public static String getIDFromIp(String ip) { + return IP_SRMNumberMap.get(ip); + } + + public static void connect(String SRMNumber, Channel channel) { + channelMap.put(SRMNumber, channel); + InetSocketAddress socketAddress = (InetSocketAddress) channel.remoteAddress(); + String clientIp = socketAddress.getAddress().getHostAddress(); + putSRMNUmber_Ip(SRMNumber, clientIp); + channelStringTime.put(SRMNumber, LocalDateTime.now()); + log.info("connect:{}巷道 ", SRMNumber); + } + + public static void sendMessage(String key, String message) { + channelMap.get(key).writeAndFlush(message); + } + + public static void disConnect(String key) { + channelMap.remove(key); + } + + public static Set keys() { + return channelMap.keySet(); + } + + public static Channel get(String key) { + return channelMap.get(key); + } + + public static void write(String data, String key) { + if (channelMap.get(key) != null) { + channelMap.get(key).writeAndFlush(data); + } else { + tcpLogger.info("no connect client:{}", key); + } + } + +} diff --git a/web/src/main/java/com/zhehekeji/web/service/client/ClientCodeMap.java b/web/src/main/java/com/zhehekeji/web/service/client/ClientCodeMap.java new file mode 100644 index 0000000..bb3a427 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/client/ClientCodeMap.java @@ -0,0 +1,24 @@ +package com.zhehekeji.web.service.client; + +import java.util.Hashtable; +import java.util.Map; + +public class ClientCodeMap { + + /** + * 线程安全的map + */ + protected static Map codeMap = new Hashtable<>(); + + public static void putCode(String SRMNumber,String code){ + codeMap.put(SRMNumber,code); + } + + public static String getCode(String SRMNumber){ + return codeMap.get(SRMNumber); + } + + public static void removeCode(String SRMNumber){ + codeMap.remove(SRMNumber); + } +} diff --git a/web/src/main/java/com/zhehekeji/web/service/client/Decoder.java b/web/src/main/java/com/zhehekeji/web/service/client/Decoder.java new file mode 100644 index 0000000..9230442 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/client/Decoder.java @@ -0,0 +1,104 @@ +package com.zhehekeji.web.service.client; + +import com.zhehekeji.web.service.EmptyCheckService; +import com.zhehekeji.web.service.PlcService; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.DelimiterBasedFrameDecoder; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.charset.Charset; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * 客户端解码器 连接用 + */ +@Slf4j +public class Decoder extends DelimiterBasedFrameDecoder { + + private static final Logger tcpLogger = LoggerFactory.getLogger("tcp"); + + public static String PT_CLIENT = "PT"; + private static String WCS_CLIENT = "WCS"; + private static String EMPTY_CLIENT = "EMPTY"; + private static String END_STRING = "$"; + + private static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,15,30, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(20000)); + + private EmptyCheckService emptyCheckService; + + private PlcService plcService; + + public Decoder(PlcService plcService,EmptyCheckService emptyCheckService) { + + super(20000,true,false, Unpooled.copiedBuffer(">".getBytes()), + Unpooled.copiedBuffer("$".getBytes())); + this.plcService = plcService; + this.emptyCheckService = emptyCheckService; + } + + @Override + protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + + in = (ByteBuf) super.decode(ctx, in); + if(in == null){ + log.debug("no data"); + return null; + } + ClientRunnable clientRunnable = new ClientRunnable(in,ctx,plcService,emptyCheckService); + threadPoolExecutor.execute(clientRunnable); + return null; + } + + public static class ClientRunnable implements Runnable { + + private ByteBuf in; + + private ChannelHandlerContext ctx; + + private PlcService plcService; + + private EmptyCheckService emptyCheckService; + + public ClientRunnable(ByteBuf in,ChannelHandlerContext ctx,PlcService plcService,EmptyCheckService emptyCheckService){ + this.ctx = ctx; + this.in = in; + this.plcService = plcService; + this.emptyCheckService = emptyCheckService; + } + + @Override + public void run() { + String body = in.toString(Charset.forName("UTF-8")); + tcpLogger.info("receive client:{}, data length:{}",body, body.length()); + Transmission transmission = new Transmission(body); + if("BP".equals(transmission.getHeader())){ + //底部拍照 + ClientChanel.sendMessage(transmission.getSRMNumber(),transmission.getBody()); + }else if("HB".equals(transmission.getHeader())){ + //心跳 + ClientChanel.connect(transmission.getSRMNumber(),ctx.channel()); + ClientChanel.sendMessage(transmission.getSRMNumber(),transmission.getBody()); + }else if("TP".equals(transmission.getHeader())){ + //顶部拍照 + ClientChanel.connect(transmission.getSRMNumber(),ctx.channel()); + ClientChanel.sendMessage(transmission.getSRMNumber(),transmission.getBody()); + }else if("LC".equals(transmission.getHeader())){ + //光源控制 + ClientChanel.connect(transmission.getSRMNumber(),ctx.channel()); + ClientChanel.sendMessage(transmission.getSRMNumber(),transmission.getBody()); + }else if("DC".equals(transmission.getHeader())){ + //断连 + ClientChanel.connect(transmission.getSRMNumber(),ctx.channel()); + ClientChanel.sendMessage(transmission.getSRMNumber(),transmission.getBody()); + } + } + } + +} \ No newline at end of file diff --git a/web/src/main/java/com/zhehekeji/web/service/client/Encoder.java b/web/src/main/java/com/zhehekeji/web/service/client/Encoder.java new file mode 100644 index 0000000..c305527 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/client/Encoder.java @@ -0,0 +1,24 @@ +package com.zhehekeji.web.service.client; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.nio.charset.StandardCharsets; + +/** + * 客户端传输协议 以>结尾 + */ +public class Encoder extends MessageToByteEncoder { + private static String END_STRING = ">"; + + private static final Logger tcpLogger = LoggerFactory.getLogger("tcp"); + @Override + protected void encode(ChannelHandlerContext channelHandlerContext, String data, ByteBuf byteBuf) throws Exception { + data = data + END_STRING; + tcpLogger.info("send to client:{}, length:{}",data, data.length()); + byteBuf.writeBytes(data.getBytes(StandardCharsets.UTF_8)); + } +} diff --git a/web/src/main/java/com/zhehekeji/web/service/client/MessageConverter.java b/web/src/main/java/com/zhehekeji/web/service/client/MessageConverter.java new file mode 100644 index 0000000..5f767f0 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/client/MessageConverter.java @@ -0,0 +1,13 @@ +package com.zhehekeji.web.service.client; + +import org.springframework.stereotype.Component; + +/** + * 普天的消息 转给盘点客户端 + */ +@Component +public class MessageConverter { + + + +} diff --git a/web/src/main/java/com/zhehekeji/web/service/client/NettyConnectHandler.java b/web/src/main/java/com/zhehekeji/web/service/client/NettyConnectHandler.java new file mode 100644 index 0000000..fe55324 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/client/NettyConnectHandler.java @@ -0,0 +1,44 @@ +package com.zhehekeji.web.service.client; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import lombok.extern.slf4j.Slf4j; + +import java.net.InetSocketAddress; + +/** + * 客户端的上下线 + * + * @author Administrator + * + */ +@Slf4j +public class NettyConnectHandler extends ChannelInboundHandlerAdapter { + + /** + * 建立连接时 + */ + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + InetSocketAddress socketAddress = (InetSocketAddress) ctx.channel().remoteAddress(); + String clientIp = socketAddress.getAddress().getHostAddress(); + int clientPort = socketAddress.getPort(); + log.info("ip:{} port:{} connected",clientIp, clientPort); + ctx.fireChannelActive(); + } + + /** + * 关闭连接时 + */ + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + InetSocketAddress socketAddress = (InetSocketAddress) ctx.channel().remoteAddress(); + String clientIp = socketAddress.getAddress().getHostAddress(); + int clientPort = socketAddress.getPort(); + log.info("ip:{} port:{} disconnected",clientIp, clientPort); + String ID = ClientChanel.getIDFromIp(clientIp); + //设置客户端下线 + ClientChanel.disConnect(ID); + } + +} diff --git a/web/src/main/java/com/zhehekeji/web/service/client/NettyServer.java b/web/src/main/java/com/zhehekeji/web/service/client/NettyServer.java new file mode 100644 index 0000000..a52bb4e --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/client/NettyServer.java @@ -0,0 +1,63 @@ +package com.zhehekeji.web.service.client; + +import com.zhehekeji.web.service.EmptyCheckService; +import com.zhehekeji.web.service.PlcService; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import lombok.SneakyThrows; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +@Component +public class NettyServer { + + @Resource + private PlcService plcService; + + @Resource + private EmptyCheckService emptyCheckService; + + public void CreateNettyServer(int port) throws InterruptedException { + Thread thread = new Thread(new Runnable() { + @SneakyThrows + @Override + public void run() { + EventLoopGroup bossGroup = new NioEventLoopGroup(); + // 从线程组, 老板线程组会把任务丢给他,让手下线程组去做任务 + EventLoopGroup workerGroup = new NioEventLoopGroup(); + + try { + // netty服务器的创建, 辅助工具类,用于服务器通道的一系列配置 + ServerBootstrap serverBootstrap = new ServerBootstrap(); + serverBootstrap.group(bossGroup, workerGroup) //绑定两个线程组 + .channel(NioServerSocketChannel.class) //指定NIO的模式 + + .childHandler(new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) { + ch.pipeline().addLast(new NettyConnectHandler()); + ch.pipeline().addLast(new Decoder(plcService,emptyCheckService)); + ch.pipeline().addLast(new Encoder()); + }}); // 子处理器,用于处理workerGroup + + // 启动server,并且设置8088为启动的端口号,同时启动方式为同步 + ChannelFuture channelFuture = serverBootstrap.bind(port).sync(); + + // 监听关闭的channel,设置位同步方式 + channelFuture.channel().closeFuture().sync(); + } finally { + //退出线程组 + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } + } + }); + thread.start(); + } +} diff --git a/web/src/main/java/com/zhehekeji/web/service/client/Transmission.java b/web/src/main/java/com/zhehekeji/web/service/client/Transmission.java new file mode 100644 index 0000000..a9111a4 --- /dev/null +++ b/web/src/main/java/com/zhehekeji/web/service/client/Transmission.java @@ -0,0 +1,48 @@ +package com.zhehekeji.web.service.client; + +import lombok.Data; + +@Data +public class Transmission { + + private String body; + private String header; + private String Split = "&"; + + private String SRMNumber; + + private Boolean success; + private String url; + private String number; + + +// public String toString(){ +// StringBuffer sb = new StringBuffer(header); +// sb.append(Split).append(SRMNumber).append(Split).append(goodsLocation).append(Split) +// .append(taskNo).append(Split).append(code) +// .append(Split).append(count).append(Split).append(visualTest); +// return sb.toString(); +// } + public Transmission(String body){ + this.body = body; + String [] strings = body.split(Split); + if(strings.length >= 2){ + this.header= strings[0]; + this.SRMNumber= strings[1]; + } + if(strings.length >= 4){ + this.success= !strings[2].equals("0"); + this.url= strings[3]; + } + if(strings.length >= 5){ + this.number= strings[4]; + } + } + public static void main(String[] args) { + Transmission t = new Transmission("TP&6&2121&UL062009050111&0&name1&25>"); + System.out.println(t); + } + public String getHeader(){ + return this.header; + } +} diff --git a/web/src/main/resources/application-prod.yml b/web/src/main/resources/application-prod.yml index 2387d7d..da18d85 100644 --- a/web/src/main/resources/application-prod.yml +++ b/web/src/main/resources/application-prod.yml @@ -7,14 +7,14 @@ spring: maxWait: 60000 minEvictableIdleTimeMillis: 300000 minIdle: 15 - password: Leaper@123 + password: root poolPreparedStatements: true testOnBorrow: true testOnReturn: false testWhileIdle: false timeBetweenEvictionRunsMillis: 60000 type: com.alibaba.druid.pool.DruidDataSource - url: jdbc:mysql://192.168.77.91:3306/lia_duoji?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 + url: jdbc:mysql://127.0.0.1:3306/lia_duoji?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true username: root validationQuery: SELECT 1 FROM DUAL # --------本服务端口号 @@ -71,6 +71,7 @@ videoStyleConfig: # -------------type 0:没有光源 1:利珀光源控制器JYDam 2:利珀视觉控制器 # ----------- num:总共多少个光源端口 index:需要控制的是哪个 # ------------ 利珀视觉控制器id从1开始,光源控制器从0开始 +serverPort: 2001 lightSource: type: 2 num: 4