package cn.unizk.service; import cn.hutool.core.date.DateUtil; import cn.unizk.constant.UniConstant; import cn.unizk.entity.dto.PrDTO; import cn.unizk.entity.po.*; import cn.unizk.mapper.*; import cn.unizk.task.DeviceTask; import cn.unizk.utils.XorUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.annotation.Resource; import java.io.IOException; import java.net.*; import java.nio.charset.StandardCharsets; import java.time.Clock; import java.time.Duration; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @Author hao * @Date 2025/9/10 16:17 * @Version 1.0 **/ @Service @Slf4j public class UdpServerService2 { // 存储设备地址的Map,Key可以是设备ID,Value是设备的IP和端口 private ConcurrentHashMap deviceMap = new ConcurrentHashMap<>(); private DatagramSocket serverSocket; private volatile boolean running = false; private ExecutorService executorService = Executors.newSingleThreadExecutor(); private static final int LISTEN_PORT = 61100; // 监听的端口 private static final long SESSION_TIMEOUT = 60_000; @Resource private UdpClientService udpClientService; @Resource private DeviceMapper deviceMapper; @Resource private DeviceUsageRecordMapper deviceUsageRecordMapper; @Resource private DeviceTask deviceTask; @Resource private DeviceUsageDetailMapper usageDetailMapper; @Resource private DeviceUpgradeMapper upgradeMapper; @Resource private FileService fileService; @Resource(name = "kryoRedisTemplate") private RedisTemplate redisTemplate; @Resource private GeneratePrService generatePrService; @Resource private UserTreatmentParametersMapper userTreatmentParametersMapper; private static final String UPGRADE_PACKAGE_ROOT_PATH = "/opt/jkadmin/upgrade/"; @Getter @Setter public static class DeviceInfo { private String muid; private String status; private String ip; private int port; private Boolean startCount = false; private final long lastActiveTime; // DeviceInfo(){} /*public boolean getStartCount(){ return this.startCount; }*/ DeviceInfo(String muid, String status, String ip, int port) { this.muid = muid; this.status = status; this.ip = ip; this.port = port; this.startCount = false; this.lastActiveTime = System.currentTimeMillis(); } DeviceInfo(String muid, String status, String ip, int port, boolean startCount) { this.muid = muid; this.status = status; this.ip = ip; this.port = port; this.startCount = startCount; this.lastActiveTime = System.currentTimeMillis(); } } @PostConstruct public void startServer() { try { serverSocket = new DatagramSocket(LISTEN_PORT); running = true; log.info("UDP Server started on port: {}", LISTEN_PORT); // 启动一个线程来持续接收消息 executorService.execute(this::receivePackets); } catch (SocketException e) { log.error("Failed to start UDP server on port: {}", LISTEN_PORT, e); } } @PreDestroy public void stopServer() { running = false; if (serverSocket != null && !serverSocket.isClosed()) { serverSocket.close(); } executorService.shutdown(); log.info("UDP Server stopped."); } /** * 持续接收UDP数据包的方法 */ private void receivePackets() { byte[] receiveBuffer = new byte[1024]; // 缓冲区 log.info("持续接收UDP数据包,{}", serverSocket.isClosed()); while (running && !serverSocket.isClosed()) { try { DatagramPacket receivePacket = new DatagramPacket(receiveBuffer, receiveBuffer.length); serverSocket.receive(receivePacket); // 阻塞等待接收数据 log.info("处理接收到的数据包"); // 处理接收到的数据包(可以在新线程中处理,避免阻塞接收) handlePacket(receivePacket); } catch (Exception e) { if (running) { // 只有在运行状态下的异常才打印 log.error("Error receiving packet: ", e); } } } } /** * 处理接收到的UDP数据包 * * @param packet 接收到的数据包 */ private void handlePacket(DatagramPacket packet) { // 获取发送方的IP和端口 String deviceIp = packet.getAddress().getHostAddress(); int devicePort = packet.getPort(); // 获取数据内容并转换为字符串 String receivedMessage = new String(packet.getData(), 0, packet.getLength()); log.info("Received from {}:{} - Message: {}", deviceIp, devicePort, receivedMessage); if (StringUtils.isBlank(receivedMessage)) { log.info("接收到的数据包为空"); return; } // 关键步骤:解析数据包,获取设备ID并注册设备地址 // 假设数据包格式为: "muid:XXXXX;Status:Online;" // 您需要根据硬件同事提供的实际协议格式进行解析 String muid = parsemuid(receivedMessage); // 需要实现此方法 String status = receivedMessage.substring(2, 3); String status2 = receivedMessage.substring(1, 3); if ("30".equals(status2)) { status = "9"; } log.info("解析得到的muid={}", muid); log.info("解析得到的状态码是:{}", status); if (muid != null && !muid.isEmpty()) { Device device = deviceMapper.selectOne(new LambdaQueryWrapper().eq(Device::getMuid, muid)); // 将设备信息存入Map System.out.println("Device registered: " + muid + " at " + deviceIp + ":" + devicePort); if (Objects.isNull(device)) { log.info("数据库不存在该设备,muid={}", muid); } else { DeviceInfo deviceInfo = deviceMap.get(muid); if ("1".equals(status)) { if (Objects.isNull(deviceInfo)) { log.info("deviceMap缓存中没有{}这个设备", muid); deviceMap.put(muid, new DeviceInfo(muid, status, deviceIp, devicePort)); } else { /*if (deviceInfo.getStartCount() && device.getSurplus() > 0 && device.getPaymentMethod() != null && device.getPaymentMethod() == 2){ log.info("设备状态为01开机,开始计数startCount为true"); int count = device.getSurplus() - 1; device.setSurplus(count); device.setExpireTime(String.valueOf(count)); deviceMapper.updateById(device); }*/ deviceMap.put(muid, new DeviceInfo(muid, status, deviceIp, devicePort)); } String lo = null, la = null; String[] items = receivedMessage.split("\\|"); for (String item : items) { if (item.startsWith("LO")) { lo = item.substring(2); } if (item.startsWith("LA")) { la = item.substring(2); } } if (lo != null && la != null) { device.setLo(lo); device.setLa(la); deviceMapper.updateById(device); } parseDeviceInfo(device, receivedMessage); /** * 拿device的硬件版本和软件版本去查device_upgrade表,如果跟当前版本不一致则升级 */ DeviceUpgrade upgrade = upgradeMapper.selectOne(new LambdaQueryWrapper().eq(DeviceUpgrade::getBeCurrentVersion, 1)); Device nDevice = deviceMapper.selectOne(new LambdaQueryWrapper().eq(Device::getMuid, muid)); if (Objects.nonNull(upgrade) && upgrade.getHVersion().equals(nDevice.getHw()) && !upgrade.getSVersion().equals(nDevice.getSw()) && nDevice.getUpgradeSwitch() == 1) { log.info("回复升级心跳,回复升级心跳回复升级心跳,回复升级心跳,回复升级心跳"); replyUpgradeHeartbeat(deviceMap.get(muid), packet); } else { /*String o = (String) redisTemplate.opsForValue().get(UniConstant.REDIS_CURRENT_DEVICE_USER + device.getDeviceId()); PrDTO prDTO = generatePrService.generatePr(o); PrDTO cachePrdto = (PrDTO) redisTemplate.opsForValue().get(o + device.getDeviceId());*/ replyHeartbeat(deviceMap.get(muid), packet, null, null); } DeviceUsageDetail detail = new DeviceUsageDetail(); detail.setStatus(status); recordUse(device, detail); if (device.getOperatingStatus() == 0) { changeDeviceOperatingStatus(device.getDeviceId(), 2); } } if ((("1".equals(status) && device.getSurplus() < 1) || device.getForcePay() == 1) && device.getFreeCount() < 1) { log.info("设备状态为1,无剩余使用次数,马上停,强制付费"); String c1 = "$01|MU{uid}|DT{date}|ON{1}|CF1|WM1|TC000|*{CRC}\r\n"; String now = DateUtil.format(new Date(), "yyyyMMddHHmmss"); String replace = c1.replace("{uid}", muid).replace("{date}", now).replace("{1}", "0"); String crc = replace.substring(1, replace.lastIndexOf("*")); String asciiHex = XorUtil.xorAsciiHex(crc); String s = replace.replace("{CRC}", asciiHex); byte[] sendData = s.getBytes(StandardCharsets.UTF_8); DatagramPacket sendPacket = new DatagramPacket( sendData, sendData.length, packet.getSocketAddress() ); try { serverSocket.send(sendPacket); } catch (IOException e) { e.printStackTrace(); } deviceMapper.updateStatus(device.getDeviceId(), "0"); } if ("2".equals(status)) { updateLastRunTime(device); String mo = parseMo(device, receivedMessage); if ((device.getSurplus() < 1 || device.getForcePay() == 1) && device.getFreeCount() < 1) { log.info("设备状态为2,五剩余使用次数,马上听"); //马上停 String c2 = "$02|MU{uid}|DT{date}|ON{1}|SN{2}|*{CRC}\r\n"; String now = DateUtil.format(new Date(), "yyyyMMddHHmmss"); String replace = c2.replace("{uid}", muid) .replace("{date}", now).replace("{1}", "0").replace("{2}", "000"); String crc = replace.substring(1, replace.lastIndexOf("*")); String asciiHex = XorUtil.xorAsciiHex(crc); String s = replace.replace("{CRC}", asciiHex); byte[] sendData = s.getBytes(StandardCharsets.UTF_8); DatagramPacket sendPacket = new DatagramPacket( sendData, sendData.length, packet.getSocketAddress() ); try { serverSocket.send(sendPacket); } catch (IOException e) { e.printStackTrace(); } deviceMap.put(muid, new DeviceInfo(muid, status, deviceIp, devicePort)); deviceMapper.updateStatus(device.getDeviceId(), "0"); } else { try { Device dvie = toLiliao(device.getDeviceId()); DeviceUsageDetail detail = parseMsgToDetail(receivedMessage); detail.setStatus(status); recordUse(dvie, detail); } catch (Exception e) { log.error("记录设备使用发生异常", e); } if (device.getPaymentMethod() == 1) { deviceMap.put(muid, new DeviceInfo(muid, status, deviceIp, devicePort)); } else { deviceMap.put(muid, new DeviceInfo(muid, status, deviceIp, devicePort, true)); } replyHeartbeat(deviceMap.get(muid), packet, null, null); } } if ("3".equals(status)) { deviceMap.put(muid, new DeviceInfo(muid, status, deviceIp, devicePort)); // String o = (String) redisTemplate.opsForValue().get(UniConstant.REDIS_CURRENT_DEVICE_USER + device.getDeviceId()); // PrDTO o1 = (PrDTO) redisTemplate.opsForValue().get(o + device.getDeviceId()); //openid是扫码开机的时候放到redis的 // String openid = (String) redisTemplate.opsForValue().get(UniConstant.DEVICE_ID + device.getDeviceId()); // String openid = "oATNGvnMbu6tbNgbnFZPWtJBGvFM"; Integer appUserId = 9; PrDTO prDTO = generatePrService.generatePr(appUserId); if (Objects.nonNull(prDTO)){ List prItems = prDTO.getPrItems(); if (CollectionUtils.isNotEmpty(prItems)){ List list = new ArrayList<>(); for (PrDTO.PrItem prItem : prItems) { UserTreatmentParameters userTreatmentParameters = new UserTreatmentParameters(); userTreatmentParameters.setAppUserId(appUserId); userTreatmentParameters.setDeviceId(device.getDeviceId()); userTreatmentParameters.setFr(prItem.getFr()); userTreatmentParameters.setPl(prItem.getPl()); userTreatmentParameters.setPw(prItem.getPw()); userTreatmentParameters.setUl(prItem.getUl()); userTreatmentParameters.setTl(prItem.getTl()); userTreatmentParameters.setCreateTime(LocalDateTime.now()); userTreatmentParameters.setStatus(1); list.add(userTreatmentParameters); } List userTreatmentParametersList = userTreatmentParametersMapper.selectList(new LambdaQueryWrapper().eq(UserTreatmentParameters::getAppUserId, appUserId)); if (CollectionUtils.isEmpty(userTreatmentParametersList)){ userTreatmentParametersMapper.insert(list); } } } replyHeartbeat(deviceMap.get(muid), packet, prDTO, null); } if (Objects.isNull(deviceInfo)) { deviceMap.put(muid, new DeviceInfo(muid, status, deviceIp, devicePort)); deviceInfo = deviceMap.get(muid); } if ("9".equals(status)) { deviceMap.put(muid, new DeviceInfo(muid,status, deviceIp, devicePort)); /*String o = (String) redisTemplate.opsForValue().get(UniConstant.REDIS_CURRENT_DEVICE_USER + device.getDeviceId()); PrDTO prDTO = (PrDTO) redisTemplate.opsForValue().get(o + device.getDeviceId());*/ String prId = null,tn = null; String[] items = receivedMessage.split("\\|"); for (String item : items) { if (item.startsWith("CI")){ prId = item.substring(2); } if (item.startsWith("TN")){ tn = item.substring(2); } } PrDTO prDTO = prId == null ? null : (PrDTO) redisTemplate.opsForValue().get(prId); if (Objects.isNull(prDTO)) { log.info("status=9,proDTO is null;status=9,proDTO is null;"); log.info("status=9,proDTO is null;status=9,proDTO is null;"); log.info("status=9,proDTO is null;status=9,proDTO is null;"); log.info("status=9,proDTO is null;status=9,proDTO is null;"); log.info("status=9,proDTO is null;status=9,proDTO is null;"); } deviceInfo = deviceMap.get(muid); replyHeartbeat(deviceInfo, packet, prDTO, tn); } /*if ("4".equals(status) && deviceInfo.getStartCount() && device.getPaymentMethod() != null && device.getPaymentMethod() == 2){ log.info("设备状态为4,关机,并且startCount为true"); int count = device.getSurplus() - 1; device.setSurplus(count); device.setExpireTime(String.valueOf(count)); deviceMapper.updateById(device); deviceMap.put(muid, new DeviceInfo(muid,status, deviceIp, devicePort)); }*/ if ("4".equals(status)) { try { Device selectById = deviceMapper.selectById(device.getDeviceId()); if (selectById.getForcePay() == 2) { Device dce = new Device(); dce.setDeviceId(device.getDeviceId()); dce.setForcePay(1); dce.setStatus("0"); deviceMapper.updateById(dce); } if (selectById.getFreeCount() > 0) { Device device5 = new Device(); device5.setDeviceId(device.getDeviceId()); device5.setFreeCount(selectById.getFreeCount() - 1); deviceMapper.updateById(device5); } changeDeviceOperatingStatus(device.getDeviceId(), 1); updateRecord(device.getDeviceId()); deleteUserTreatParam(device.getDeviceId()); } catch (Exception e) { log.error("更新设备使用记录发生异常", e); } } } } //马上回 /*log.info("马上回------"); // udpClientService.sendCommand(deviceIp,devicePort,"$01|MU000B57020457D4B100001F97|DT20251011163826|ON0|CF1|WM1|TC000|*7E\r\n"); InetAddress address = null; try { address = InetAddress.getByName(deviceIp); } catch (UnknownHostException e) { e.printStackTrace(); } String c01 = "$01|MU{uid}|DT{date}|ON{1}|CF1|WM1|TC000|*{CRC}\r\n"; String c02 = "$02|MU{uid}|DT{date}|ON{1}|SN{2}|*{CRC}\r\n"; String command = "$01|MU000B57020457D4B100001F97|DT20251011163826|ON0|CF1|WM1|TC000|*7E\r\n"; if ("1".equals(status)){ String now = DateUtil.format(new Date(), "yyyyMMddHHmmss"); String replace = c01.replace("{uid}", muid).replace("{date}", now).replace("{1}", "0"); String crc = replace.substring(1, replace.lastIndexOf("*")); String asciiHex = XorUtil.xorAsciiHex(crc); command = replace.replace("{CRC}", asciiHex); } if ("2".equals(status)){ String now = DateUtil.format(new Date(), "yyyyMMddHHmmss"); String replace = c02.replace("{uid}", muid) .replace("{date}", now).replace("{1}", "0").replace("{2}", "000"); String crc = replace.substring(1, replace.lastIndexOf("*")); String asciiHex = XorUtil.xorAsciiHex(crc); command = replace.replace("{CRC}", asciiHex); } byte[] sendData = command.getBytes(StandardCharsets.UTF_8); DatagramPacket sendPacket = new DatagramPacket( sendData, sendData.length, packet.getSocketAddress() ); try { serverSocket.send(sendPacket); } catch (IOException e) { e.printStackTrace(); }*/ } private void getPR(DeviceInfo info, DatagramPacket packet) { if (Objects.isNull(info)) { log.info("method->replyHeartbeat,DeviceInfo is null"); return; } String c01 = "$01|MU{uid}|DT{date}|ON{1}|CF{2}|WM1|TC000|PR1|*{CRC}\r\n"; } private void replyHeartbeat(DeviceInfo info, DatagramPacket packet, PrDTO prDTO, String tn) { if (Objects.isNull(info)) { log.info("method->replyHeartbeat,DeviceInfo is null"); return; } String status = info.getStatus(); String muid = info.getMuid(); switch (status) { case "1": String c01 = "$01|MU{uid}|DT{date}|ON{1}|CF{2}|WM1|TC000|*{CRC}\r\n"; Device device = deviceMapper.selectOne(new LambdaQueryWrapper().eq(Device::getMuid, muid)); String now = DateUtil.format(new Date(), "yyyyMMddHHmmss"); String replace = c01.replace("{uid}", muid).replace("{date}", now) .replace("{1}", device.getStatus()).replace("{2}", device.getCf()); String crc = replace.substring(1, replace.lastIndexOf("*")); String asciiHex = XorUtil.xorAsciiHex(crc); String message = replace.replace("{CRC}", asciiHex); byte[] sendData = message.getBytes(StandardCharsets.UTF_8); DatagramPacket sendPacket = new DatagramPacket( sendData, sendData.length, packet.getSocketAddress() ); try { serverSocket.send(sendPacket); } catch (IOException e) { e.printStackTrace(); } break; case "2": String c02 = "$02|MU{uid}|DT{date}|ON{1}|SN{2}|*{CRC}\r\n"; Device device2 = deviceMapper.selectOne(new LambdaQueryWrapper().eq(Device::getMuid, muid)); String now2 = DateUtil.format(new Date(), "yyyyMMddHHmmss"); String replace2 = c02.replace("{uid}", muid) .replace("{date}", now2).replace("{1}", device2.getStatus()).replace("{2}", "000"); String crc2 = replace2.substring(1, replace2.lastIndexOf("*")); String asciiHex2 = XorUtil.xorAsciiHex(crc2); String msg = replace2.replace("{CRC}", asciiHex2); byte[] sendData2 = msg.getBytes(StandardCharsets.UTF_8); DatagramPacket sendPacket2 = new DatagramPacket( sendData2, sendData2.length, packet.getSocketAddress() ); try { serverSocket.send(sendPacket2); } catch (IOException e) { e.printStackTrace(); } break; case "3": String defaultDm = "$03|MU{uid}|CI00|CN00|*{CRC}\r\n"; String dm = "$03|MU{uid}|CI{1}|CN{2}|*{CRC}\r\n"; if (prDTO == null) { log.info("status==3;prDTO=====null;===null"); defaultDm = defaultDm.replace("{uid}", muid); String substring = defaultDm.substring(1, defaultDm.lastIndexOf("*")); String xorAsciiHex = XorUtil.xorAsciiHex(substring); String s = defaultDm.replace("{CRC}", xorAsciiHex); byte[] sendData3 = s.getBytes(StandardCharsets.UTF_8); DatagramPacket datagramPacket = new DatagramPacket(sendData3, sendData3.length, packet.getSocketAddress()); try { serverSocket.send(datagramPacket); } catch (IOException e) { e.printStackTrace(); } } else { log.info("prDTO========={}", prDTO.toString()); redisTemplate.opsForValue().set(prDTO.getId(),prDTO,2,TimeUnit.HOURS); List prItems = prDTO.getPrItems(); String cn = prItems.size() > 9 ? String.valueOf(prItems.size()) : "0"+prItems.size(); dm = dm.replace("{uid}", muid).replace("{1}", prDTO.getId()).replace("{2}", cn); String substring = dm.substring(1, dm.lastIndexOf("*")); String xorAsciiHex = XorUtil.xorAsciiHex(substring); String s = dm.replace("{CRC}", xorAsciiHex); log.info("回传给硬件的数据:{}",s); byte[] sendData3 = s.getBytes(StandardCharsets.UTF_8); DatagramPacket datagramPacket = new DatagramPacket(sendData3, sendData3.length, packet.getSocketAddress()); try { serverSocket.send(datagramPacket); } catch (IOException e) { e.printStackTrace(); } } break; case "9": if (prDTO == null) { log.info("case=9,prDTO=null;case=9,prDTO=null;case=9,prDTO=null;case=9,prDTO=null;case=9,prDTO=null;case=9,prDTO=null"); String defaultDm9 = "$03|MU{uid}|CI123456790123456|TN01|FR03|PW50|PL08|UL01|TL017|*{CRC}\r\n"; String substring = defaultDm9.substring(1, defaultDm9.lastIndexOf("*")); String xorAsciiHex = XorUtil.xorAsciiHex(substring); String s = defaultDm9.replace("{CRC}", xorAsciiHex); byte[] sendData3 = s.getBytes(StandardCharsets.UTF_8); DatagramPacket datagramPacket = new DatagramPacket(sendData3, sendData3.length, packet.getSocketAddress()); try { serverSocket.send(datagramPacket); } catch (IOException e) { e.printStackTrace(); } } else { List prItems = prDTO.getPrItems(); // String tn1 = tn.substring(1); PrDTO.PrItem prItem = prItems.get(Integer.parseInt(tn)-1); String cm9 = "$30|MU{uid}|CI{1}|TN{2}|FR{3}|PW{4}|PL{5}|UL{6}|TL{7}|*{CRC}\r\n"; cm9 = cm9.replace("{uid}", muid).replace("{1}", prDTO.getId()).replace("{2}", tn) .replace("{3}", prItem.getFr()).replace("{4}", prItem.getPw()).replace("{5}", prItem.getPl()) .replace("{6}", prItem.getUl()).replace("{7}", prItem.getTl()); String substring = cm9.substring(1, cm9.lastIndexOf("*")); String xorAsciiHex = XorUtil.xorAsciiHex(substring); String s = cm9.replace("{CRC}", xorAsciiHex); log.info("status=9,远程下参数数据是:{}",s); byte[] sendData3 = s.getBytes(StandardCharsets.UTF_8); DatagramPacket datagramPacket = new DatagramPacket(sendData3, sendData3.length, packet.getSocketAddress()); try { serverSocket.send(datagramPacket); } catch (IOException e) { e.printStackTrace(); } } } } private void parseDeviceInfo(Device device, String msg) { String[] items = msg.split("\\|"); String hw = null, sw = null, ic = null, cf = null, wm = null, cr = null; for (String item : items) { if (item.startsWith("HW")) { hw = item.substring(2); } if (item.startsWith("SW")) { sw = item.substring(2); } if (item.startsWith("IC")) { ic = item.substring(2); } if (item.startsWith("CF")) { cf = item.substring(2); } if (item.startsWith("WM")) { wm = item.substring(2); } if (item.startsWith("CR")) { cr = item.substring(2); } } device.setHw(hw); device.setSw(sw); device.setIccid(ic); if (StringUtils.isBlank(device.getCf())) { device.setCf(cf); } device.setWm(wm); device.setCr(cr); device.setOperatingStatus(3); device.setLastRunTime(LocalDateTime.now()); log.info("解析设备回复的数据得到的硬件版本为:{},软件版本为:{}",hw,sw); log.info("服务器上的LocalDateTime.now()========={}", LocalDateTime.now()); deviceMapper.updateDeviceInfo(device); } private String parseMo(Device device, String msg) { String[] items = msg.split("\\|"); String mo = null; for (String item : items) { if (item.startsWith("MO")) { mo = item.substring(2); break; } } device.setMo(mo); Device dv = new Device(); dv.setDeviceId(device.getDeviceId()); dv.setMo(mo); deviceMapper.updateById(dv); return mo; } private void deleteUserTreatParam(String deviceId){ LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.set(UserTreatmentParameters::getStatus,0).eq(UserTreatmentParameters::getDeviceId,deviceId); userTreatmentParametersMapper.update(updateWrapper); } private DeviceUsageDetail parseMsgToDetail(String msg) { String[] items = msg.split("\\|"); String fr = null, pw = null, pl = null, ul = null, wt = null; for (String item : items) { if (item.startsWith("FR")) { fr = item.substring(2); } if (item.startsWith("PW")) { pw = item.substring(2); } if (item.startsWith("PL")) { pl = item.substring(2); } if (item.startsWith("UL")) { ul = item.substring(2); } if (item.startsWith("WT")) { wt = item.substring(2); } } DeviceUsageDetail detail = new DeviceUsageDetail(); detail.setFr(fr); detail.setPw(pw); detail.setPl(pl); detail.setUl(ul); detail.setWt(wt); return detail; } private void updateLastRunTime(Device device) { Device d = new Device(); d.setDeviceId(device.getDeviceId()); d.setLastRunTime(LocalDateTime.now()); deviceMapper.updateById(d); } private Device toLiliao(String deviceId) { Device device = deviceMapper.selectById(deviceId); if (Objects.nonNull(device) && device.getOperatingStatus() != 0) { device.setOperatingStatus(0); deviceMapper.updateById(device); deviceTask.addAbnormalDeviceTask(deviceId); } return device; } private void recordUse(Device device, DeviceUsageDetail detail) { if (Objects.isNull(device)) { log.info("设备使用记录recordUse->device is null"); return; } device.setLastRunTime(LocalDateTime.now()); deviceMapper.updateById(device); DeviceUsageRecord record = deviceUsageRecordMapper.selectOne(new LambdaQueryWrapper() .eq(DeviceUsageRecord::getDeviceId, device.getDeviceId()) .eq(DeviceUsageRecord::getStartUseFlag, "true")); if (Objects.nonNull(record)) { DeviceUsageDetail selectOne = usageDetailMapper.selectOne(new LambdaQueryWrapper(). eq(DeviceUsageDetail::getUsageRecordId, record.getId()).orderByDesc(DeviceUsageDetail::getCreateTime).last("limit 1")); long seconds = Duration.between(selectOne.getCreateTime(), LocalDateTime.now()).getSeconds(); if ("2".equals(detail.getStatus()) && seconds > 30) { detail.setUsageRecordId(record.getId()); detail.setCreateTime(LocalDateTime.now()); usageDetailMapper.insert(detail); } log.info("设备正在运行"); return; } DeviceUsageRecord deviceUsageRecord = new DeviceUsageRecord(); deviceUsageRecord.setStartTime(LocalDateTime.now(Clock.systemDefaultZone())); deviceUsageRecord.setDeviceId(device.getDeviceId()); deviceUsageRecord.setStartUseFlag(UniConstant.CONST_TRUE); deviceUsageRecord.setWm(device.getWm()); deviceUsageRecord.setBelong(device.getAffiliatedWith()); deviceUsageRecord.setMuid(device.getMuid()); deviceUsageRecordMapper.insert(deviceUsageRecord); if (device.getPaymentMethod() != null && device.getPaymentMethod() == 2 && device.getSurplus() > 0) { int count = device.getSurplus() - 1; device.setSurplus(count); device.setExpireTime(String.valueOf(count)); deviceMapper.updateById(device); } detail.setCreateTime(LocalDateTime.now()); detail.setUsageRecordId(deviceUsageRecord.getId()); /* 如果真要记录待机状态,那么把status=1的recordUse()放开,在这里加上根据deviceUsageRecord.getId()和status=1去查DeviceUsageDetail表, 如果不为空则不插入数据,即只记录一条状态为待机的数据 */ List usageDetails = usageDetailMapper.selectList(new LambdaQueryWrapper().eq(DeviceUsageDetail::getUsageRecordId, deviceUsageRecord.getId()) .eq(DeviceUsageDetail::getStatus, "1")); if (CollectionUtils.isEmpty(usageDetails)) { usageDetailMapper.insert(detail); } } private void updateRecord(String deviceId) { DeviceUsageRecord deviceUsageRecord = deviceUsageRecordMapper.selectOne(new LambdaQueryWrapper() .eq(DeviceUsageRecord::getDeviceId, deviceId) .eq(DeviceUsageRecord::getStartUseFlag, UniConstant.CONST_TRUE)); if (Objects.isNull(deviceUsageRecord)) { return; } deviceUsageRecord.setEndTime(LocalDateTime.now(Clock.systemDefaultZone())); deviceUsageRecord.setStartUseFlag(UniConstant.CONST_FALSE); deviceUsageRecordMapper.updateById(deviceUsageRecord); DeviceUsageDetail detail = new DeviceUsageDetail(); detail.setStatus("4"); detail.setCreateTime(LocalDateTime.now()); detail.setUsageRecordId(deviceUsageRecord.getId()); usageDetailMapper.insert(detail); } private void changeDeviceOperatingStatus(String deviceId, Integer status) { Device device = deviceMapper.selectById(deviceId); if (Objects.isNull(device)) { return; } device.setOperatingStatus(status); deviceMapper.updateById(device); } public boolean reply(Device device, String status) { String muid = device.getMuid(); DeviceInfo deviceInfo = getDeviceInfo(muid); if (deviceInfo == null) { log.info("设备会话已过期,请设备先上报一次数据"); return false; } String message = ""; String c01 = "$01|MU{uid}|DT{date}|ON{1}|CF{2}|WM1|TC000|*{CRC}\r\n"; String c02 = "$02|MU{uid}|DT{date}|ON{1}|SN{2}|*{CRC}\r\n"; if ("1".equals(deviceInfo.getStatus())) { String now = DateUtil.format(new Date(), "yyyyMMddHHmmss"); String replace = c01.replace("{uid}", muid).replace("{date}", now).replace("{1}", status).replace("{2}", device.getCf()); String crc = replace.substring(1, replace.lastIndexOf("*")); String asciiHex = XorUtil.xorAsciiHex(crc); message = replace.replace("{CRC}", asciiHex); } else if ("2".equals(deviceInfo.getStatus())) { String now = DateUtil.format(new Date(), "yyyyMMddHHmmss"); String replace = c02.replace("{uid}", muid) .replace("{date}", now).replace("{1}", status).replace("{2}", "000"); String crc = replace.substring(1, replace.lastIndexOf("*")); String asciiHex = XorUtil.xorAsciiHex(crc); message = replace.replace("{CRC}", asciiHex); } else if ("4".equals(deviceInfo.getStatus())) { log.info("设备为关机状态"); return true; } try { log.info("最终发送的数据为:{}", message); byte[] data = message.getBytes(StandardCharsets.UTF_8); InetAddress address = InetAddress.getByName(deviceInfo.getIp()); DatagramPacket packet = new DatagramPacket(data, data.length, address, deviceInfo.getPort()); serverSocket.send(packet); System.out.printf("UDP Reply sent to %s:%d -> %s%n", deviceInfo.getIp(), deviceInfo.getPort(), message); device.setStatus(status); deviceMapper.updateById(device); return true; } catch (IOException e) { log.info("回复设备UDP消息发送异常"); e.printStackTrace(); return true; } } /** * 根据实际协议解析设备ID * 这是一个示例,您需要根据硬件数据包的实际格式重写此方法 * * @param message 收到的原始消息 * @return 解析出的设备ID */ private String parsemuid(String message) { Pattern p = Pattern.compile("^[^|]*\\|([^|]+)\\|"); Matcher matcher = p.matcher(message); if (matcher.find()) { return matcher.group(1).substring(2); } // 如果协议里没有ID,也可以用IP:端口作为唯一标识 return null; } /** * 获取设备信息 * * @param muid 设备ID * @return 设备信息,如果不存在则返回null */ public DeviceInfo getDeviceInfo(String muid) { DeviceInfo deviceInfo = deviceMap.get(muid); if (Objects.nonNull(deviceInfo) && System.currentTimeMillis() - deviceInfo.getLastActiveTime() < SESSION_TIMEOUT) { return deviceInfo; } else { if (Objects.isNull(deviceInfo)) { deviceMap.remove(muid); return null; } if (!deviceInfo.getStartCount()) { deviceMap.remove(muid); } return null; } } /** * 获取所有在线设备 */ public ConcurrentHashMap getOnlineDevices() { return new ConcurrentHashMap<>(deviceMap); } private void replyUpgradeHeartbeat(DeviceInfo info, DatagramPacket packet) { if (Objects.isNull(info)) { log.info("method->replyHeartbeat,DeviceInfo is null"); return; } String c01 = "$01|MU{uid}|DT{date}|ON{1}|CF{2}|UP{3}|WM1|TC000|*{CRC}\r\n"; /* 查device_upgrade表拿到当前版本升级包名称,然后去/opt/jkadmin/upgrade下面拿到升级包 调fileService拆包,得到包数量,替换{3},然后回复 */ DeviceUpgrade deviceUpgrade = upgradeMapper.selectOne(new LambdaQueryWrapper().eq(DeviceUpgrade::getBeCurrentVersion, 1)); if (Objects.isNull(deviceUpgrade)) { replyHeartbeat(info, packet, null, null); } else { String fileUrl = UPGRADE_PACKAGE_ROOT_PATH + deviceUpgrade.getFileName(); List packages = null; try { packages = fileService.splitFileWithNIO(fileUrl); } catch (IOException e) { log.error("拆包发生异常-.-.-.-.-.-.-.-.-.", e); } if (CollectionUtils.isNotEmpty(packages) && packages.size() <= 32) { String muid = info.getMuid(); Device device = deviceMapper.selectOne(new LambdaQueryWrapper().eq(Device::getMuid, muid)); if (Objects.isNull(device)) { return; } String now = DateUtil.format(new Date(), "yyyyMMddHHmmss"); String r = c01.replace("{uid}", muid).replace("{date}", now).replace("{1}", device.getStatus()) .replace("{2}", device.getCf()).replace("{3}", String.valueOf(packages.size())); String crc = r.substring(1, r.lastIndexOf("*")); String xorAsciiHex = XorUtil.xorAsciiHex(crc); String msg = r.replace("{CRC}", xorAsciiHex); log.info("回复升级心跳数据:{}", msg); byte[] bytes = msg.getBytes(StandardCharsets.UTF_8); DatagramPacket sendPacket = new DatagramPacket(bytes, bytes.length, packet.getSocketAddress()); try { serverSocket.send(sendPacket); } catch (IOException e) { log.error("回复升级心跳包发生异常", e); } } else { log.info("升级包为空或升级包大小超过1024*32"); replyHeartbeat(info, packet, null, null); } } } }