package com.ruoyi.sim.service.impl; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.sim.config.SimConfig; import com.ruoyi.sim.config.SimDebugConfig; import com.ruoyi.sim.domain.Seat; import com.ruoyi.sim.domain.vo.SimSocketParamVo; import com.ruoyi.sim.domain.vo.SocketWrapCacheVo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.parameters.P; import org.springframework.stereotype.Service; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.util.HashMap; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import static com.ruoyi.sim.constant.CommConst.SOCKET_TIME_OUT; /** * 1.所有Socket常开。 * todo: connectedTimeMillis 时间。 *

* 创建Socket重试,用failed开始方法去操作。 */ @Service public class SocketService { private static final Logger l = LoggerFactory.getLogger(SocketService.class); /** * 硬件孙总北京办公室,测试远程地址 *

* 孙总办公室域名 * nas.yichiot.com */ public static final String IP_TEST = "221.218.215.73"; public static final int PORT_TEST = 8899; private static final int LOCAL_PORT = 9000; private static final int INIT_SIZE = 32; /** * 6 hours * 1000L * 60 * 60 * 6 * 1000L * 60 * 5 */ private static final long TIMEOUT_LIMIT = 1000L * 60 * 60 * 6; public static final int SOCKET_CONNECT_RETRY_COUNT_LIMIT = 4; /** * key: ip:port * value: */ private static HashMap cachedMap = new HashMap<>(INIT_SIZE); /** * 每个Socket都有。 * 重试次数。 * default 0. */ private static HashMap failedMap = new HashMap<>(INIT_SIZE); @Autowired private SimConfig config; @Autowired private SeatService seatService; /** * @param sspv * @return true:socket ok! */ public boolean isOk(final SimSocketParamVo sspv) { final String key = sspv.toKey(); if (cachedMap.containsKey(key) && cachedMap.get(key) != null) { Socket s = cachedMap.get(key).getSocket(); if (s != null) { return (s.isConnected() && s.isBound() && !s.isClosed()); } } return false; } public boolean isNotOk(final SimSocketParamVo sspv) { return !isOk(sspv); } /** * 暂时不考虑超时周期。 * todo: * * @param ssv * @return */ public boolean isTimeout(SimSocketParamVo ssv) { if (cachedMap.containsKey(ssv.toKey())) { Long cached = cachedMap.get(ssv.toKey()).getOkTimeMillis(); if (cached == null || cached == 0L) { return true; } return System.currentTimeMillis() - cached <= TIMEOUT_LIMIT; } return true; } /** * @param sspv * @param force 是否强制使用新连接的Socket * @return */ public AjaxResult openOne(final SimSocketParamVo sspv, final boolean force) { // check. if (!config.isCommGlobal()) { l.warn("isCommGlobal == {} [模拟器通信被禁用!]", config.isCommGlobal()); return AjaxResult.error("模拟器通信被禁用!"); } // try { if (isNotOk(sspv) || force) { final String key = sspv.toKey(); l.info("openSocket cachedSocket is not ok!try new socket ip = {}:{}!forceNew = {}", sspv.getIp(), sspv.getPort(), force); closeOne(sspv, false); // 不指定本地端口实现。 Socket s = new Socket(sspv.getIp(), sspv.getPort()); // todo: LOCAL_PORT // Socket s = new Socket(sspv.getIp(), sspv.getPort(), InetAddress.getLocalHost(), SimDebugConfig.TCP_LOCAL_PORT); s.setSoTimeout(SOCKET_TIME_OUT); SocketWrapCacheVo value = new SocketWrapCacheVo(sspv.getIp(), sspv.getPort(), s, System.currentTimeMillis()); // 新建Socket需要挂起 2s后再发指令。 value.setPreviousSendSleep(2000L); cachedMap.put(key, value); // socket failed count reset. failedReset0(sspv); } else { l.info("openSocket cachedSocket cache ok!cached socket ip = {}:{}!forceNew = {}", sspv.getIp(), sspv.getPort(), force); } Seat seat = seatService.uniqueByRs485IpAndPort(sspv.getIp(), sspv.getPort()); seat.setSeatRs485SocketState(Seat.SocketState.ONLINE); seatService.updateSeat(seat); return AjaxResult.success("Socket[" + sspv.getIp() + ":" + sspv.getPort() + "],创建成功!"); } catch (IOException e) { l.error("IOException = {}", sspv); if (failedIsReachedMax(sspv, SOCKET_CONNECT_RETRY_COUNT_LIMIT)) { return AjaxResult.error("Socket[" + sspv.getIp() + ":" + sspv.getPort() + "],重试[" + failedGet(sspv) + "]次,创建失败!"); } else { failedPlus1(sspv); AjaxResult ar = openOne(sspv, force); if (ar.isSuccess()) { return ar; } else { return AjaxResult.error("Socket[" + sspv.getIp() + ":" + sspv.getPort() + "],进行重试,创建失败!"); } } } } public AjaxResult tryOpenOne(final SimSocketParamVo sspv) { if (!config.isCommGlobal()) { l.warn("isCommGlobal == {} [模拟器通信被禁用!]", config.isCommGlobal()); return AjaxResult.error("模拟器通信被禁用!"); } return openOne(sspv, false); } /** * todo:部分返回Aj结果。 * * @return */ public AjaxResult tryOpenAll() { if (!config.isCommGlobal()) { l.warn("isCommGlobal == {} [模拟器通信被禁用!]", config.isCommGlobal()); return AjaxResult.error("模拟器通信被禁用!"); } List allSeat = seatService.listAllEnable(); for (Seat s : allSeat) { AjaxResult ar = openOne(s.toSimSocketParamVo(), false); l.debug("AjaxResult = {}", ar); } return AjaxResult.success("所有Socket,创建成功!"); } public AjaxResult closeOne(final SimSocketParamVo sspv, boolean failedReset) { if (!config.isCommGlobal()) { l.warn("isCommGlobal == {} [模拟器通信被禁用!]", config.isCommGlobal()); return AjaxResult.error("模拟器通信被禁用!"); } final String key = sspv.toKey(); try { if (cachedMap.containsKey(key)) { Socket s = cachedMap.get(key).getSocket(); s.getInputStream().close(); s.getOutputStream().close(); s.close(); } } catch (IOException e) { e.printStackTrace(); } finally { if (failedReset) { // failed count reset. failedReset0(sspv); } cachedMap.remove(key); return AjaxResult.success("关闭Socket成功!"); } } public AjaxResult closeAll() { if (!config.isCommGlobal()) { l.warn("isCommGlobal == {} [模拟器通信被禁用!]", config.isCommGlobal()); return AjaxResult.error("模拟器通信被禁用!"); } final String msgOk = "关闭所有Socket成功!"; List allSeat = seatService.listAllEnable(); for (Seat s : allSeat) { closeOne(new SimSocketParamVo(s.getSeatRs485Ip(), s.getSeatRs485Port()), true); } return AjaxResult.success(msgOk); } /** * @param seat * @return todo:null */ public SocketWrapCacheVo get(final Seat seat) { if (seat == null) { throw new IllegalArgumentException("seat为空。"); } return get(new SimSocketParamVo(seat.getSeatRs485Ip(), seat.getSeatRs485Port())); } /** * @param sspv * @return */ public SocketWrapCacheVo get(final SimSocketParamVo sspv) { if (isNotOk(sspv)) { AjaxResult ar = openOne(sspv, true); if (ar.isError()) { // todo: isError } } return cachedMap.get(sspv.toKey()); } /** * 初始化。todo: */ public void clear(final SimSocketParamVo sspv) { } private static final int SOCKET_FAILED_COUNT_0 = 0; private static final int SOCKET_FAILED_COUNT_ADD_1 = 1; /** * @param sspv * @param limit include limit * @return */ public boolean failedIsReachedMax(final SimSocketParamVo sspv, final int limit) { final String key = sspv.toKey(); return (failedMap.containsKey(key) && failedMap.get(key).get() >= limit); } public int failedPlus1(final SimSocketParamVo sspv) { final String key = sspv.toKey(); if (!failedMap.containsKey(key)) { failedMap.put(key, new AtomicInteger(SOCKET_FAILED_COUNT_ADD_1)); } else { failedMap.get(key).addAndGet(SOCKET_FAILED_COUNT_ADD_1); } return failedMap.get(key).get(); } public int failedGet(final SimSocketParamVo sspv) { final String key = sspv.toKey(); if (failedMap.containsKey(key)) { return failedMap.get(key).get(); } else { return SOCKET_FAILED_COUNT_0; } } public void failedReset0(final SimSocketParamVo sspv) { final String key = sspv.toKey(); if (failedMap.containsKey(key)) { failedMap.get(key).set(SOCKET_FAILED_COUNT_0); } else { l.debug("not containsKey SimSocketParamVo sspv:" + sspv); } } }