| | |
| | | <artifactId>poi-ooxml</artifactId> |
| | | <version>3.13</version> |
| | | </dependency> |
| | | |
| | | <dependency> |
| | | <groupId>de.schlichtherle.truelicense</groupId> |
| | | <artifactId>truelicense-core</artifactId> |
| | | <version>1.33</version> |
| | | </dependency> |
| | | </dependencies> |
| | | |
| | | <build> |
| New file |
| | |
| | | package com.zy.asrs.controller; |
| | | |
| | | import com.core.annotations.ManagerAuth; |
| | | import com.core.common.Cools; |
| | | import com.core.common.R; |
| | | import com.zy.common.web.BaseController; |
| | | import com.zy.core.enums.SlaveType; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import javax.servlet.http.HttpServletResponse; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Path; |
| | | import java.nio.file.Paths; |
| | | import java.util.*; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @RestController |
| | | public class DeviceLogController extends BaseController { |
| | | |
| | | @Value("${deviceLogStorage.loggingPath}") |
| | | private String loggingPath; |
| | | |
| | | private static class ProgressInfo { |
| | | long totalRaw; |
| | | long processedRaw; |
| | | int totalCount; |
| | | int processedCount; |
| | | boolean finished; |
| | | } |
| | | |
| | | private static final Map<String, ProgressInfo> DOWNLOAD_PROGRESS = new ConcurrentHashMap<>(); |
| | | |
| | | @RequestMapping(value = "/deviceLog/dates/auth") |
| | | @ManagerAuth |
| | | public R dates() { |
| | | try { |
| | | Path baseDir = Paths.get(loggingPath); |
| | | if (!Files.exists(baseDir)) { |
| | | return R.ok(new ArrayList<>()); |
| | | } |
| | | List<String> days = Files.list(baseDir) |
| | | .filter(Files::isDirectory) |
| | | .map(p -> p.getFileName().toString()) |
| | | .filter(name -> name.length() == 8 && name.chars().allMatch(Character::isDigit)) |
| | | .sorted() |
| | | .collect(Collectors.toList()); |
| | | Map<String, Map<String, List<String>>> grouped = new LinkedHashMap<>(); |
| | | for (String day : days) { |
| | | String year = day.substring(0, 4); |
| | | String month = day.substring(4, 6); |
| | | grouped.computeIfAbsent(year, k -> new LinkedHashMap<>()) |
| | | .computeIfAbsent(month, k -> new ArrayList<>()) |
| | | .add(day); |
| | | } |
| | | List<Map<String, Object>> tree = new ArrayList<>(); |
| | | for (Map.Entry<String, Map<String, List<String>>> yEntry : grouped.entrySet()) { |
| | | Map<String, Object> yNode = new HashMap<>(); |
| | | yNode.put("title", yEntry.getKey()); |
| | | yNode.put("id", yEntry.getKey()); |
| | | List<Map<String, Object>> mChildren = new ArrayList<>(); |
| | | for (Map.Entry<String, List<String>> mEntry : yEntry.getValue().entrySet()) { |
| | | Map<String, Object> mNode = new HashMap<>(); |
| | | mNode.put("title", mEntry.getKey()); |
| | | mNode.put("id", yEntry.getKey() + "-" + mEntry.getKey()); |
| | | List<Map<String, Object>> dChildren = new ArrayList<>(); |
| | | for (String d : mEntry.getValue()) { |
| | | Map<String, Object> dNode = new HashMap<>(); |
| | | dNode.put("title", d.substring(6, 8)); |
| | | dNode.put("id", d); |
| | | dNode.put("day", d); |
| | | dChildren.add(dNode); |
| | | } |
| | | mNode.put("children", dChildren); |
| | | mChildren.add(mNode); |
| | | } |
| | | yNode.put("children", mChildren); |
| | | tree.add(yNode); |
| | | } |
| | | return R.ok(tree); |
| | | } catch (Exception e) { |
| | | return R.error("读取日期失败"); |
| | | } |
| | | } |
| | | |
| | | @RequestMapping(value = "/deviceLog/day/{day}/devices/auth") |
| | | @ManagerAuth |
| | | public R devices(@PathVariable("day") String day) { |
| | | try { |
| | | if (day == null || day.length() != 8 || !day.chars().allMatch(Character::isDigit)) { |
| | | return R.error("日期格式错误"); |
| | | } |
| | | Path dayDir = Paths.get(loggingPath, day); |
| | | if (!Files.exists(dayDir) || !Files.isDirectory(dayDir)) { |
| | | return R.ok(new ArrayList<>()); |
| | | } |
| | | List<Path> files = Files.list(dayDir) |
| | | .filter(p -> !Files.isDirectory(p) && p.getFileName().toString().endsWith(".log")) |
| | | .collect(Collectors.toList()); |
| | | Map<String, Map<String, Object>> deviceMap = new HashMap<>(); |
| | | for (Path p : files) { |
| | | String name = p.getFileName().toString(); |
| | | String[] parts = name.split("_"); |
| | | if (parts.length < 4) { |
| | | continue; |
| | | } |
| | | String deviceNo = parts[1]; |
| | | String type = parts[0]; |
| | | Map<String, Object> info = deviceMap.computeIfAbsent(deviceNo, k -> { |
| | | Map<String, Object> map = new HashMap<>(); |
| | | map.put("deviceNo", deviceNo); |
| | | map.put("types", new HashSet<String>()); |
| | | map.put("fileCount", 0); |
| | | return map; |
| | | }); |
| | | ((Set<String>) info.get("types")).add(type); |
| | | info.put("fileCount", ((Integer) info.get("fileCount")) + 1); |
| | | } |
| | | List<Map<String, Object>> res = deviceMap.values().stream().map(m -> { |
| | | Map<String, Object> x = new HashMap<>(); |
| | | x.put("deviceNo", m.get("deviceNo")); |
| | | x.put("types", ((Set<String>) m.get("types")).stream().collect(Collectors.toList())); |
| | | x.put("fileCount", m.get("fileCount")); |
| | | return x; |
| | | }).collect(Collectors.toList()); |
| | | return R.ok(res); |
| | | } catch (Exception e) { |
| | | return R.error("读取设备列表失败"); |
| | | } |
| | | } |
| | | |
| | | @RequestMapping(value = "/deviceLog/day/{day}/download/auth") |
| | | @ManagerAuth |
| | | public void download(@PathVariable("day") String day, |
| | | @RequestParam("type") String type, |
| | | @RequestParam("deviceNo") String deviceNo, |
| | | @RequestParam(value = "offset", required = false) Integer offset, |
| | | @RequestParam(value = "limit", required = false) Integer limit, |
| | | @RequestParam(value = "progressId", required = false) String progressId, |
| | | HttpServletResponse response) { |
| | | try { |
| | | String dayClean = day == null ? null : day.replaceAll("\\D", ""); |
| | | if (dayClean == null || dayClean.length() != 8 || !dayClean.chars().allMatch(Character::isDigit)) { |
| | | response.setStatus(400); |
| | | return; |
| | | } |
| | | if (type == null || SlaveType.findInstance(type) == null) { |
| | | response.setStatus(400); |
| | | return; |
| | | } |
| | | if (deviceNo == null || !deviceNo.chars().allMatch(Character::isDigit)) { |
| | | response.setStatus(400); |
| | | return; |
| | | } |
| | | Path dayDir = Paths.get(loggingPath, dayClean); |
| | | if (!Files.exists(dayDir) || !Files.isDirectory(dayDir)) { |
| | | response.setStatus(404); |
| | | return; |
| | | } |
| | | List<Path> files = Files.list(dayDir) |
| | | .filter(p -> { |
| | | String name = p.getFileName().toString(); |
| | | String prefix = type + "_" + deviceNo + "_" + dayClean + "_"; |
| | | return name.endsWith(".log") && name.startsWith(prefix); |
| | | }).collect(Collectors.toList()); |
| | | // 排序(按文件中的索引号递增) |
| | | String prefix = type + "_" + deviceNo + "_" + dayClean + "_"; |
| | | files.sort(Comparator.comparingInt(p -> { |
| | | String n = p.getFileName().toString(); |
| | | try { |
| | | String suf = n.substring(prefix.length(), n.length() - 4); |
| | | return Integer.parseInt(suf); |
| | | } catch (Exception e) { |
| | | return Integer.MAX_VALUE; |
| | | } |
| | | })); |
| | | int from = offset == null || offset < 0 ? 0 : offset; |
| | | int max = limit == null || limit <= 0 ? 200 : limit; |
| | | int to = Math.min(files.size(), from + max); |
| | | if (from >= files.size()) { |
| | | response.setStatus(404); |
| | | return; |
| | | } |
| | | files = files.subList(from, to); |
| | | if (files.isEmpty()) { |
| | | response.setStatus(404); |
| | | return; |
| | | } |
| | | ProgressInfo info; |
| | | String id = progressId; |
| | | if (Cools.isEmpty(id)) { |
| | | id = UUID.randomUUID().toString(); |
| | | } |
| | | List<Path> finalFiles = files; |
| | | info = DOWNLOAD_PROGRESS.computeIfAbsent(id, k -> { |
| | | ProgressInfo x = new ProgressInfo(); |
| | | x.totalCount = finalFiles.size(); |
| | | long sum = 0L; |
| | | for (Path f : finalFiles) { |
| | | try { sum += Files.size(f); } catch (Exception ignored) {} |
| | | } |
| | | x.totalRaw = sum; |
| | | x.processedRaw = 0L; |
| | | x.processedCount = 0; |
| | | x.finished = false; |
| | | return x; |
| | | }); |
| | | response.reset(); |
| | | response.setContentType("application/zip"); |
| | | String filename = type + "_" + deviceNo + "_" + dayClean + ".zip"; |
| | | response.setHeader("Content-Disposition", "attachment; filename=" + filename); |
| | | long totalRawSize = 0L; |
| | | for (Path f : files) { |
| | | try { totalRawSize += Files.size(f); } catch (Exception ignored) {} |
| | | } |
| | | response.setHeader("X-Total-Size", String.valueOf(totalRawSize)); |
| | | response.setHeader("X-File-Count", String.valueOf(files.size())); |
| | | response.setHeader("X-Progress-Id", id); |
| | | try (java.util.zip.ZipOutputStream zos = new java.util.zip.ZipOutputStream(response.getOutputStream())) { |
| | | for (Path f : files) { |
| | | java.util.zip.ZipEntry entry = new java.util.zip.ZipEntry(f.getFileName().toString()); |
| | | zos.putNextEntry(entry); |
| | | Files.copy(f, zos); |
| | | zos.closeEntry(); |
| | | try { |
| | | info.processedRaw += Files.size(f); |
| | | } catch (Exception ignored) {} |
| | | info.processedCount += 1; |
| | | } |
| | | zos.finish(); |
| | | info.finished = true; |
| | | } |
| | | } catch (Exception e) { |
| | | try { response.setStatus(500); } catch (Exception ignore) {} |
| | | } |
| | | } |
| | | |
| | | @RequestMapping(value = "/deviceLog/download/init/auth") |
| | | @ManagerAuth |
| | | public R init(@org.springframework.web.bind.annotation.RequestBody com.alibaba.fastjson.JSONObject param) { |
| | | try { |
| | | String day = param.getString("day"); |
| | | String type = param.getString("type"); |
| | | String deviceNo = param.getString("deviceNo"); |
| | | Integer offset = param.getInteger("offset"); |
| | | Integer limit = param.getInteger("limit"); |
| | | String dayClean = Cools.isEmpty(day) ? null : day.replaceAll("\\D", ""); |
| | | if (Cools.isEmpty(dayClean) || dayClean.length() != 8 || !dayClean.chars().allMatch(Character::isDigit)) { |
| | | return R.error("日期格式错误"); |
| | | } |
| | | if (Cools.isEmpty(type) || SlaveType.findInstance(type) == null) { |
| | | return R.error("设备类型错误"); |
| | | } |
| | | if (Cools.isEmpty(deviceNo) || !deviceNo.chars().allMatch(Character::isDigit)) { |
| | | return R.error("设备编号错误"); |
| | | } |
| | | Path dayDir = Paths.get(loggingPath, dayClean); |
| | | if (!Files.exists(dayDir) || !Files.isDirectory(dayDir)) { |
| | | return R.error("当日目录不存在"); |
| | | } |
| | | List<Path> files = Files.list(dayDir) |
| | | .filter(p -> { |
| | | String name = p.getFileName().toString(); |
| | | String prefix = type + "_" + deviceNo + "_" + dayClean + "_"; |
| | | return name.endsWith(".log") && name.startsWith(prefix); |
| | | }).collect(Collectors.toList()); |
| | | String prefix = type + "_" + deviceNo + "_" + dayClean + "_"; |
| | | files.sort(Comparator.comparingInt(p -> { |
| | | String n = p.getFileName().toString(); |
| | | try { |
| | | String suf = n.substring(prefix.length(), n.length() - 4); |
| | | return Integer.parseInt(suf); |
| | | } catch (Exception e) { |
| | | return Integer.MAX_VALUE; |
| | | } |
| | | })); |
| | | int from = offset == null || offset < 0 ? 0 : offset; |
| | | int max = limit == null || limit <= 0 ? 200 : limit; |
| | | int to = Math.min(files.size(), from + max); |
| | | if (from >= files.size()) { |
| | | return R.error("起始序号超出范围"); |
| | | } |
| | | files = files.subList(from, to); |
| | | String id = UUID.randomUUID().toString(); |
| | | ProgressInfo info = new ProgressInfo(); |
| | | info.totalCount = files.size(); |
| | | long sum = 0L; |
| | | for (Path f : files) { |
| | | try { sum += Files.size(f); } catch (Exception ignored) {} |
| | | } |
| | | info.totalRaw = sum; |
| | | info.processedRaw = 0L; |
| | | info.processedCount = 0; |
| | | info.finished = false; |
| | | DOWNLOAD_PROGRESS.put(id, info); |
| | | Map<String, Object> res = new HashMap<>(); |
| | | res.put("progressId", id); |
| | | res.put("totalSize", info.totalRaw); |
| | | res.put("fileCount", info.totalCount); |
| | | return R.ok(res); |
| | | } catch (Exception e) { |
| | | return R.error("初始化失败"); |
| | | } |
| | | } |
| | | |
| | | @RequestMapping(value = "/deviceLog/download/progress/auth") |
| | | @ManagerAuth |
| | | public R progress(String id) { |
| | | ProgressInfo info = DOWNLOAD_PROGRESS.get(id); |
| | | if (info == null) { |
| | | return R.error("无效进度"); |
| | | } |
| | | long total = info.totalRaw; |
| | | long done = info.processedRaw; |
| | | int percent; |
| | | if (info.finished) { |
| | | percent = 100; |
| | | } else if (total > 0) { |
| | | percent = (int) Math.min(99, (done * 100L) / total); |
| | | } else if (info.totalCount > 0) { |
| | | percent = (int) Math.min(99, (info.processedCount * 100L) / info.totalCount); |
| | | } else { |
| | | percent = 0; |
| | | } |
| | | Map<String, Object> res = new HashMap<>(); |
| | | res.put("percent", percent); |
| | | res.put("processedSize", done); |
| | | res.put("totalSize", total); |
| | | res.put("processedCount", info.processedCount); |
| | | res.put("totalCount", info.totalCount); |
| | | res.put("finished", info.finished); |
| | | return R.ok(res); |
| | | } |
| | | } |
| | |
| | | @TableField("out_station_list") |
| | | private String outStationList; |
| | | |
| | | /** |
| | | * 最大入库任务数 |
| | | */ |
| | | @ApiModelProperty(value= "最大入库任务数") |
| | | @TableField("max_in_task") |
| | | private Integer maxInTask; |
| | | |
| | | /** |
| | | * 最大出库任务数 |
| | | */ |
| | | @ApiModelProperty(value= "最大出库任务数") |
| | | @TableField("max_out_task") |
| | | private Integer maxOutTask; |
| | | |
| | | public BasCrnp() {} |
| | | |
| | | public BasCrnp(Integer status,Integer wrkNo,String inEnable,String outEnable,Long createBy,Date createTime,Long updateBy,Date updateTime,String memo) { |
| | |
| | | import com.core.common.Arith; |
| | | import com.core.common.Cools; |
| | | import com.core.common.SpringUtils; |
| | | import com.core.exception.CoolException; |
| | | import com.zy.asrs.entity.BasCrnp; |
| | | import com.zy.asrs.entity.WrkMast; |
| | | import com.zy.asrs.service.BasCrnpService; |
| | |
| | | |
| | | private static final String LOC_NO_FLAG = "-"; |
| | | private static final DecimalFormat fmt = new DecimalFormat("##0.00"); |
| | | private static Integer defaultDeviceLogCollectTime = 200; |
| | | private static Long lastUpdateDeviceLogCollectTime = -1L; |
| | | |
| | | public static float scale(Float f){ |
| | | if (f == null || f == 0f || Float.isNaN(f)) { |
| | |
| | | |
| | | //获取设备日志采集时间 |
| | | public static int getDeviceLogCollectTime() { |
| | | int defaultTime = 200; |
| | | try { |
| | | RedisUtil redisUtil = SpringUtils.getBean(RedisUtil.class); |
| | | if (redisUtil == null) { |
| | | return defaultTime; |
| | | } |
| | | if (defaultDeviceLogCollectTime == null || System.currentTimeMillis() - lastUpdateDeviceLogCollectTime > 60 * 1000) { |
| | | int defaultTime = 200; |
| | | lastUpdateDeviceLogCollectTime = System.currentTimeMillis(); |
| | | try { |
| | | RedisUtil redisUtil = null; |
| | | try { |
| | | redisUtil = SpringUtils.getBean(RedisUtil.class); |
| | | } catch (CoolException coolException) { |
| | | |
| | | Object object = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key); |
| | | if (object == null) { |
| | | return defaultTime; |
| | | } |
| | | } |
| | | if (redisUtil == null) { |
| | | return defaultTime; |
| | | } |
| | | |
| | | HashMap<String, String> systemConfigMap = (HashMap<String, String>) object; |
| | | String deviceLogCollectTime = systemConfigMap.get("deviceLogCollectTime"); |
| | | if(deviceLogCollectTime == null){ |
| | | return defaultTime; |
| | | } |
| | | Object object = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key); |
| | | if (object == null) { |
| | | return defaultTime; |
| | | } |
| | | |
| | | return Integer.parseInt(deviceLogCollectTime); |
| | | }catch (Exception e){ |
| | | e.printStackTrace(); |
| | | HashMap<String, String> systemConfigMap = (HashMap<String, String>) object; |
| | | String deviceLogCollectTime = systemConfigMap.get("deviceLogCollectTime"); |
| | | if(deviceLogCollectTime == null){ |
| | | return defaultTime; |
| | | } |
| | | |
| | | return Integer.parseInt(deviceLogCollectTime); |
| | | }catch (Exception e){ |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | return defaultTime; |
| | | return defaultDeviceLogCollectTime; |
| | | } |
| | | |
| | | //获取入库任务可用排 |
| | |
| | | String USER_10002 = "10002-账号已被禁用"; |
| | | String USER_10003 = "10003-密码错误"; |
| | | |
| | | String SYSTEM_20001 = "20001-许可证已失效"; |
| | | |
| | | } |
| | |
| | | import com.zy.common.utils.RandomValidateCodeUtil; |
| | | import com.zy.system.entity.*; |
| | | import com.zy.system.service.*; |
| | | import com.zy.system.timer.LicenseTimer; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | |
| | | private PermissionService permissionService; |
| | | @Autowired |
| | | private RolePermissionService rolePermissionService; |
| | | @Autowired |
| | | private LicenseTimer licenseTimer; |
| | | |
| | | @RequestMapping("/login.action") |
| | | @ManagerAuth(value = ManagerAuth.Auth.NONE, memo = "登录") |
| | | public R loginAction(String mobile, String password){ |
| | | //验证许可证是否有效 |
| | | if (!licenseTimer.getSystemSupport()){ |
| | | return R.parse(CodeRes.SYSTEM_20001); |
| | | } |
| | | if (mobile.equals("super") && password.equals(Cools.md5(superPwd))) { |
| | | Map<String, Object> res = new HashMap<>(); |
| | | res.put("username", mobile); |
| | |
| | | package com.zy.core; |
| | | |
| | | import com.core.common.SpringUtils; |
| | | import com.core.exception.CoolException; |
| | | import com.zy.core.plugin.MainProcessPluginApi; |
| | | import com.zy.core.properties.SystemProperties; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | if (mainProcessPluginApi == null) { |
| | | String className = mainProcessPlugin.contains(".") ? mainProcessPlugin : "com.zy.core.plugin." + mainProcessPlugin; |
| | | Class<? extends MainProcessPluginApi> clazz = Class.forName(className).asSubclass(MainProcessPluginApi.class); |
| | | mainProcessPluginApi = SpringUtils.getBean(clazz); |
| | | try { |
| | | mainProcessPluginApi = SpringUtils.getBean(clazz); |
| | | } catch (CoolException coolException) { |
| | | continue; |
| | | } |
| | | } |
| | | |
| | | // 系统运行状态判断 |
| | |
| | | } |
| | | |
| | | public ZyCrnStatusEntity getStatus() { |
| | | if (zyCrnConnectApi == null) { |
| | | return null; |
| | | } |
| | | return zyCrnConnectApi.getStatus(); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | public ZyRgvStatusEntity getStatus() { |
| | | if (zyRgvConnectApi == null) { |
| | | return null; |
| | | } |
| | | return zyRgvConnectApi.getStatus(); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | public List<ZyStationStatusEntity> getStatus() { |
| | | if (zyStationConnectApi == null) { |
| | | return null; |
| | | } |
| | | return zyStationConnectApi.getStatus(); |
| | | } |
| | | |
| | |
| | | return; |
| | | } |
| | | |
| | | String barcodeTime = String.valueOf(System.currentTimeMillis()); |
| | | String barcode = barcodeTime.substring(5); |
| | | |
| | | status.setTaskNo(taskNo); |
| | | status.setLoading(true); |
| | | status.setBarcode(String.valueOf(System.currentTimeMillis())); |
| | | status.setBarcode(barcode); |
| | | } |
| | | |
| | | private void generateFakeOutStationData(Integer stationId) { |
| | |
| | | if(connect.IsSuccess){ |
| | | connected = true; |
| | | OutputQueue.CRN.offer(MessageFormat.format( "【{0}】堆垛机plc连接成功 ===>> [id:{1}] [ip:{2}] [port:{3}]", DateUtils.convert(new Date()), deviceConfig.getDeviceNo(), deviceConfig.getIp(), deviceConfig.getPort())); |
| | | News.info("SiemensCrn"+" - 1"+" - 堆垛机plc连接成功 ===>> [id:{}] [ip:{}] [port:{}]", deviceConfig.getDeviceNo(), deviceConfig.getIp(), deviceConfig.getPort()); |
| | | News.info("SiemensCrn 堆垛机plc连接成功 ===>> [id:{}] [ip:{}] [port:{}]", deviceConfig.getDeviceNo(), deviceConfig.getIp(), deviceConfig.getPort()); |
| | | } else { |
| | | OutputQueue.CRN.offer(MessageFormat.format("【{0}】堆垛机plc连接失败!!! ===>> [id:{1}] [ip:{2}] [port:{3}]", DateUtils.convert(new Date()), deviceConfig.getDeviceNo(), deviceConfig.getIp(), deviceConfig.getPort())); |
| | | News.error("SiemensCrn"+" - 2"+" - 堆垛机plc连接失败!!! ===>> [id:{}] [ip:{}] [port:{}]", deviceConfig.getDeviceNo(), deviceConfig.getIp(), deviceConfig.getPort()); |
| | | News.error("SiemensCrn 堆垛机plc连接失败!!! ===>> [id:{}] [ip:{}] [port:{}]", deviceConfig.getDeviceNo(), deviceConfig.getIp(), deviceConfig.getPort()); |
| | | } |
| | | // siemensNet.ConnectClose(); |
| | | return connected; |
| | |
| | | return crnStatus; |
| | | } else { |
| | | OutputQueue.CRN.offer(MessageFormat.format("【{0}】读取堆垛机plc状态信息失败 ===>> [id:{1}] [ip:{2}] [port:{3}]", DateUtils.convert(new Date()), deviceConfig.getDeviceNo(), deviceConfig.getIp(), deviceConfig.getPort())); |
| | | News.error("SiemensCrn"+" - 4"+" - 读取堆垛机plc状态信息失败 ===>> [id:{}] [ip:{}] [port:{}]", deviceConfig.getDeviceNo(), deviceConfig.getIp(), deviceConfig.getPort()); |
| | | News.error("SiemensCrn 读取堆垛机plc状态信息失败 ===>> [id:{}] [ip:{}] [port:{}]", deviceConfig.getDeviceNo(), deviceConfig.getIp(), deviceConfig.getPort()); |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | OutputQueue.CRN.offer(MessageFormat.format("【{0}】读取堆垛机plc状态信息失败 ===>> [id:{1}] [ip:{2}] [port:{3}]", DateUtils.convert(new Date()), deviceConfig.getDeviceNo(), deviceConfig.getIp(), deviceConfig.getPort())); |
| | | News.error("SiemensCrn"+" - 5"+" - 读取堆垛机plc状态信息失败 ===>> [id:{}] [ip:{}] [port:{}]", deviceConfig.getDeviceNo(), deviceConfig.getIp(), deviceConfig.getPort()); |
| | | News.error("SiemensCrn 读取垛机plc状态信息失败 ===>> [id:{}] [ip:{}] [port:{}]", deviceConfig.getDeviceNo(), deviceConfig.getIp(), deviceConfig.getPort()); |
| | | } |
| | | return null; |
| | | } |
| | |
| | | } |
| | | |
| | | if (result != null && result.IsSuccess) { |
| | | News.info("SiemensCrn" + " - 7" + " - 堆垛机命令下发[id:{}] >>>>> {}", command.getCrnNo(), JSON.toJSON(command)); |
| | | News.info("SiemensCrn堆垛机命令下发[id:{}] >>>>> {}", command.getCrnNo(), JSON.toJSON(command)); |
| | | OutputQueue.CRN.offer(MessageFormat.format("【{0}】[id:{1}] >>>>> 命令下发: {2}", DateUtils.convert(new Date()), command.getCrnNo(), JSON.toJSON(command))); |
| | | response.setResult(true); |
| | | response.setMessage("命令下发成功"); |
| | | } else { |
| | | News.error("SiemensCrn" + " - 8" + " - 写入堆垛机plc数据失败 ===>> [id:{}]", command.getCrnNo()); |
| | | News.error("SiemensCrn写入堆垛机plc数据失败 ===>> [id:{}]", command.getCrnNo()); |
| | | OutputQueue.CRN.offer(MessageFormat.format("【{0}】写入堆垛机plc数据失败 ===>> [id:{1}]", DateUtils.convert(new Date()), command.getCrnNo())); |
| | | response.setResult(false); |
| | | response.setMessage("命令下发失败"); |
| | |
| | | rgvProtocol.setDeviceDataLog(System.currentTimeMillis()); |
| | | } |
| | | |
| | | BasRgvService basRgvService = SpringUtils.getBean(BasRgvService.class); |
| | | BasRgvService basRgvService = null; |
| | | try { |
| | | basRgvService = SpringUtils.getBean(BasRgvService.class); |
| | | } catch (Exception e) { |
| | | |
| | | } |
| | | if (basRgvService != null) { |
| | | BasRgv basRgv = basRgvService.selectOne(new EntityWrapper<BasRgv>().eq("rgv_no", deviceConfig.getDeviceNo())); |
| | | if(basRgv == null) { |
| | |
| | | ZyCrnStatusEntity crnStatus = zyCrnConnectDriver.getStatus(); |
| | | if (crnStatus == null) { |
| | | OutputQueue.CRN.offer(MessageFormat.format("【{0}】读取堆垛机plc状态信息失败 ===>> [id:{1}] [ip:{2}] [port:{3}]", DateUtils.convert(new Date()), deviceConfig.getDeviceNo(), deviceConfig.getIp(), deviceConfig.getPort())); |
| | | News.error("SiemensCrn"+" - 5"+" - 读取堆垛机plc状态信息失败 ===>> [id:{}] [ip:{}] [port:{}]", deviceConfig.getDeviceNo(), deviceConfig.getIp(), deviceConfig.getPort()); |
| | | News.error("SiemensCrn读取堆垛机plc状态信息失败 ===>> [id:{}] [ip:{}] [port:{}]", deviceConfig.getDeviceNo(), deviceConfig.getIp(), deviceConfig.getPort()); |
| | | return; |
| | | } |
| | | |
| | |
| | | crnProtocol.setDeviceDataLog(System.currentTimeMillis()); |
| | | } |
| | | |
| | | BasCrnpService basCrnpService = SpringUtils.getBean(BasCrnpService.class); |
| | | BasCrnpService basCrnpService = null; |
| | | try { |
| | | basCrnpService = SpringUtils.getBean(BasCrnpService.class); |
| | | }catch (Exception e){ |
| | | |
| | | } |
| | | if (basCrnpService != null) { |
| | | BasCrnp basCrnp = basCrnpService.selectOne(new EntityWrapper<BasCrnp>().eq("crn_no", deviceConfig.getDeviceNo())); |
| | | if(basCrnp == null) { |
| | |
| | | basCrnp.setStatus(1); |
| | | basCrnp.setInEnable("N"); |
| | | basCrnp.setOutEnable("N"); |
| | | basCrnp.setMaxInTask(5); |
| | | basCrnp.setMaxOutTask(5); |
| | | basCrnp.setCreateTime(new Date()); |
| | | basCrnpService.insert(basCrnp); |
| | | } |
| | |
| | | } |
| | | |
| | | if(statusList.isEmpty()) { |
| | | BasDevpService basDevpService = SpringUtils.getBean(BasDevpService.class); |
| | | BasDevpService basDevpService = null; |
| | | try { |
| | | basDevpService = SpringUtils.getBean(BasDevpService.class); |
| | | } catch (Exception e) { |
| | | |
| | | } |
| | | if (basDevpService == null) { |
| | | return; |
| | | } |
| | |
| | | return; |
| | | } |
| | | |
| | | List<WrkMast> inWrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>() |
| | | .eq("crn_no", basCrnp.getCrnNo()) |
| | | .eq("io_type", WrkIoType.IN.id) |
| | | ); |
| | | // 检查是否超过最大入库任务数 |
| | | if(inWrkMasts.size() >= basCrnp.getMaxInTask()){ |
| | | News.info("堆垛机:{} 已达最大入库任务数,当前任务数:{}", basCrnp.getCrnNo(), inWrkMasts.size()); |
| | | return; |
| | | } |
| | | |
| | | Integer crnNo = basCrnp.getCrnNo(); |
| | | |
| | | for (StationObjModel stationObjModel : inStationList) { |
| | |
| | | return; |
| | | } |
| | | |
| | | List<WrkMast> outWrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>() |
| | | .eq("crn_no", basCrnp.getCrnNo()) |
| | | .eq("io_type", WrkIoType.OUT.id) |
| | | ); |
| | | // 检查是否超过最大出库任务数 |
| | | if(outWrkMasts.size() >= basCrnp.getMaxOutTask()){ |
| | | News.info("堆垛机:{} 已达最大出库任务数,当前任务数:{}", basCrnp.getCrnNo(), outWrkMasts.size()); |
| | | return; |
| | | } |
| | | |
| | | Integer crnNo = basCrnp.getCrnNo(); |
| | | |
| | | List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>() |
| New file |
| | |
| | | package com.zy.system.controller; |
| | | |
| | | import com.core.common.Cools; |
| | | import com.core.common.R; |
| | | import com.zy.system.entity.license.*; |
| | | import com.zy.system.timer.LicenseTimer; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.http.MediaType; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import java.io.File; |
| | | import java.io.IOException; |
| | | |
| | | /** |
| | | * |
| | | * 用于生成证书文件,不能放在给客户部署的代码里 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/license") |
| | | public class LicenseCreatorController { |
| | | |
| | | @Value("${license.licensePath}") |
| | | private String licensePath; |
| | | @Autowired |
| | | private LicenseCheckListener licenseCheckListener; |
| | | @Autowired |
| | | private LicenseTimer licenseTimer; |
| | | /** |
| | | * 获取服务器硬件信息 |
| | | * @param osName 操作系统类型,如果为空则自动判断 |
| | | */ |
| | | @RequestMapping(value = "/getServerInfos",produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}) |
| | | public LicenseCheck getServerInfos(@RequestParam(value = "osName",required = false) String osName) { |
| | | //操作系统类型 |
| | | if(Cools.isEmpty(osName)){ |
| | | osName = System.getProperty("os.name"); |
| | | } |
| | | osName = osName.toLowerCase(); |
| | | |
| | | AbstractServerInfos abstractServerInfos = null; |
| | | |
| | | //根据不同操作系统类型选择不同的数据获取方法 |
| | | if (osName.startsWith("windows")) { |
| | | abstractServerInfos = new WindowsServerInfos(); |
| | | } else if (osName.startsWith("linux")) { |
| | | abstractServerInfos = new LinuxServerInfos(); |
| | | }else{//其他服务器类型 |
| | | abstractServerInfos = new WindowsServerInfos(); |
| | | } |
| | | |
| | | return abstractServerInfos.getServerInfos(); |
| | | } |
| | | |
| | | /** |
| | | * 获取许可证有效期天数 |
| | | */ |
| | | @RequestMapping(value = "/getLicenseDays") |
| | | public R getLicenseDays() { |
| | | return R.ok(licenseTimer.getLicenseDays()); |
| | | } |
| | | |
| | | @RequestMapping(value = "/updateLicense") |
| | | public R updateLicense(@RequestParam("file") MultipartFile[] files){ |
| | | MultipartFile file = files[0]; |
| | | |
| | | String licensePathFileName = this.getClass().getClassLoader().getResource(licensePath).getPath(); |
| | | File licensePathFile = new File(licensePathFileName); |
| | | //服务器端保存的文件对象 |
| | | File serverFile = new File(licensePathFile.getPath()); |
| | | if (serverFile.exists()) { |
| | | try { |
| | | serverFile.delete();//存在文件,删除 |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | try { |
| | | //创建文件 |
| | | serverFile.createNewFile(); |
| | | //将上传的文件写入到服务器端文件内 |
| | | file.transferTo(serverFile); |
| | | } catch (IOException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | |
| | | //重新加载许可证 |
| | | boolean loadedLicense = licenseCheckListener.loadLicense(); |
| | | if (loadedLicense) { |
| | | return R.ok(); |
| | | } |
| | | return R.error("许可证更新失败"); |
| | | } |
| | | |
| | | @RequestMapping(value = "/activate") |
| | | public R activate() { |
| | | licenseTimer.timer(); |
| | | return R.ok(); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.zy.system.entity; |
| | | |
| | | import com.core.common.Cools;import com.baomidou.mybatisplus.annotations.TableId; |
| | | import com.baomidou.mybatisplus.enums.IdType; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | import com.baomidou.mybatisplus.annotations.TableField; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import com.baomidou.mybatisplus.annotations.TableName; |
| | | import java.io.Serializable; |
| | | |
| | | @Data |
| | | @TableName("sys_license_infos") |
| | | public class LicenseInfos implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | @ApiModelProperty(value= "") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Integer id; |
| | | |
| | | @ApiModelProperty(value= "") |
| | | private String license; |
| | | |
| | | @ApiModelProperty(value= "") |
| | | private String licenseTime; |
| | | |
| | | @ApiModelProperty(value= "") |
| | | @TableField("create_time") |
| | | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") |
| | | private Date createTime; |
| | | |
| | | public LicenseInfos() {} |
| | | |
| | | public LicenseInfos(String license,Date createTime) { |
| | | this.license = license; |
| | | this.createTime = createTime; |
| | | } |
| | | |
| | | // LicenseInfos licenseInfos = new LicenseInfos( |
| | | // null, // |
| | | // null // |
| | | // ); |
| | | |
| | | public String getCreateTime$(){ |
| | | if (Cools.isEmpty(this.createTime)){ |
| | | return ""; |
| | | } |
| | | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.createTime); |
| | | } |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | package com.zy.system.entity.license; |
| | | |
| | | import org.apache.logging.log4j.LogManager; |
| | | import org.apache.logging.log4j.Logger; |
| | | |
| | | import java.net.InetAddress; |
| | | import java.net.NetworkInterface; |
| | | import java.net.SocketException; |
| | | import java.util.ArrayList; |
| | | import java.util.Enumeration; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 用于获取客户服务器的基本信息,如:IP、Mac地址、CPU序列号、主板序列号等 |
| | | */ |
| | | public abstract class AbstractServerInfos { |
| | | private static Logger logger = LogManager.getLogger(AbstractServerInfos.class); |
| | | |
| | | /** |
| | | * 组装需要额外校验的License参数 |
| | | */ |
| | | public LicenseCheck getServerInfos(){ |
| | | LicenseCheck result = new LicenseCheck(); |
| | | |
| | | try { |
| | | // result.setIpAddress(this.getIpAddress()); |
| | | result.setMacAddress(this.getMacAddress()); |
| | | result.setCpuSerial(this.getCPUSerial()); |
| | | result.setMainBoardSerial(this.getMainBoardSerial()); |
| | | }catch (Exception e){ |
| | | logger.error("获取服务器硬件信息失败",e); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 获取IP地址 |
| | | */ |
| | | protected abstract List<String> getIpAddress() throws Exception; |
| | | |
| | | /** |
| | | * 获取Mac地址 |
| | | */ |
| | | protected abstract List<String> getMacAddress() throws Exception; |
| | | |
| | | /** |
| | | * 获取CPU序列号 |
| | | */ |
| | | protected abstract String getCPUSerial() throws Exception; |
| | | |
| | | /** |
| | | * 获取主板序列号 |
| | | */ |
| | | protected abstract String getMainBoardSerial() throws Exception; |
| | | |
| | | /** |
| | | * 获取当前服务器所有符合条件的InetAddress |
| | | */ |
| | | protected List<InetAddress> getLocalAllInetAddress() throws Exception { |
| | | List<InetAddress> result = new ArrayList<>(4); |
| | | |
| | | // 遍历所有的网络接口 |
| | | for (Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); networkInterfaces.hasMoreElements(); ) { |
| | | NetworkInterface iface = (NetworkInterface) networkInterfaces.nextElement(); |
| | | // 在所有的接口下再遍历IP |
| | | for (Enumeration inetAddresses = iface.getInetAddresses(); inetAddresses.hasMoreElements(); ) { |
| | | InetAddress inetAddr = (InetAddress) inetAddresses.nextElement(); |
| | | |
| | | //排除LoopbackAddress、SiteLocalAddress、LinkLocalAddress、MulticastAddress类型的IP地址 |
| | | if(!inetAddr.isLoopbackAddress() /*&& !inetAddr.isSiteLocalAddress()*/ |
| | | && !inetAddr.isLinkLocalAddress() && !inetAddr.isMulticastAddress()){ |
| | | result.add(inetAddr); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 获取某个网络接口的Mac地址 |
| | | */ |
| | | protected String getMacByInetAddress(InetAddress inetAddr){ |
| | | try { |
| | | byte[] mac = NetworkInterface.getByInetAddress(inetAddr).getHardwareAddress(); |
| | | if (mac == null) { |
| | | return null; |
| | | } |
| | | |
| | | StringBuffer stringBuffer = new StringBuffer(); |
| | | |
| | | for(int i=0;i<mac.length;i++){ |
| | | if(i != 0) { |
| | | stringBuffer.append("-"); |
| | | } |
| | | |
| | | //将十六进制byte转化为字符串 |
| | | String temp = Integer.toHexString(mac[i] & 0xff); |
| | | if(temp.length() == 1){ |
| | | stringBuffer.append("0" + temp); |
| | | }else{ |
| | | stringBuffer.append(temp); |
| | | } |
| | | } |
| | | |
| | | return stringBuffer.toString().toUpperCase(); |
| | | } catch (SocketException e) { |
| | | e.printStackTrace(); |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.zy.system.entity.license; |
| | | |
| | | import de.schlichtherle.license.AbstractKeyStoreParam; |
| | | |
| | | import java.io.*; |
| | | |
| | | /** |
| | | * 自定义KeyStoreParam,用于将公私钥存储文件存放到其他磁盘位置而不是项目中 |
| | | */ |
| | | public class CustomKeyStoreParam extends AbstractKeyStoreParam { |
| | | |
| | | /** |
| | | * 公钥/私钥在磁盘上的存储路径 |
| | | */ |
| | | private String storePath; |
| | | private String alias; |
| | | private String storePwd; |
| | | private String keyPwd; |
| | | |
| | | public CustomKeyStoreParam(Class clazz, String resource, String alias, String storePwd, String keyPwd) { |
| | | super(clazz, resource); |
| | | this.storePath = resource; |
| | | this.alias = alias; |
| | | this.storePwd = storePwd; |
| | | this.keyPwd = keyPwd; |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public String getAlias() { |
| | | return alias; |
| | | } |
| | | |
| | | @Override |
| | | public String getStorePwd() { |
| | | return storePwd; |
| | | } |
| | | |
| | | @Override |
| | | public String getKeyPwd() { |
| | | return keyPwd; |
| | | } |
| | | |
| | | /** |
| | | * 复写de.schlichtherle.license.AbstractKeyStoreParam的getStream()方法<br/> |
| | | * 用于将公私钥存储文件存放到其他磁盘位置而不是项目中 |
| | | */ |
| | | @Override |
| | | public InputStream getStream() throws IOException { |
| | | final InputStream in = this.getClass().getClassLoader().getResourceAsStream(storePath); |
| | | if (null == in) { |
| | | throw new FileNotFoundException(storePath); |
| | | } |
| | | |
| | | return in; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.system.entity.license; |
| | | |
| | | import com.core.common.Cools; |
| | | import de.schlichtherle.license.*; |
| | | import de.schlichtherle.xml.GenericCertificate; |
| | | import org.apache.logging.log4j.LogManager; |
| | | import org.apache.logging.log4j.Logger; |
| | | |
| | | import java.beans.XMLDecoder; |
| | | import java.io.BufferedInputStream; |
| | | import java.io.ByteArrayInputStream; |
| | | import java.io.UnsupportedEncodingException; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 自定义LicenseManager,用于增加额外的服务器硬件信息校验 |
| | | */ |
| | | public class CustomLicenseManager extends LicenseManager{ |
| | | private static Logger logger = LogManager.getLogger(CustomLicenseManager.class); |
| | | |
| | | //XML编码 |
| | | private static final String XML_CHARSET = "UTF-8"; |
| | | //默认BUFSIZE |
| | | private static final int DEFAULT_BUFSIZE = 8 * 1024; |
| | | |
| | | public CustomLicenseManager() { |
| | | |
| | | } |
| | | |
| | | public CustomLicenseManager(LicenseParam param) { |
| | | super(param); |
| | | } |
| | | |
| | | /** |
| | | * 复写create方法 |
| | | */ |
| | | @Override |
| | | protected synchronized byte[] create( |
| | | LicenseContent content, |
| | | LicenseNotary notary) |
| | | throws Exception { |
| | | initialize(content); |
| | | this.validateCreate(content); |
| | | final GenericCertificate certificate = notary.sign(content); |
| | | return getPrivacyGuard().cert2key(certificate); |
| | | } |
| | | |
| | | /** |
| | | * 复写install方法,其中validate方法调用本类中的validate方法,校验IP地址、Mac地址等其他信息 |
| | | */ |
| | | @Override |
| | | protected synchronized LicenseContent install( |
| | | final byte[] key, |
| | | final LicenseNotary notary) |
| | | throws Exception { |
| | | final GenericCertificate certificate = getPrivacyGuard().key2cert(key); |
| | | |
| | | notary.verify(certificate); |
| | | final LicenseContent content = (LicenseContent)this.load(certificate.getEncoded()); |
| | | this.validate(content); |
| | | setLicenseKey(key); |
| | | setCertificate(certificate); |
| | | |
| | | return content; |
| | | } |
| | | |
| | | /** |
| | | * 复写verify方法,调用本类中的validate方法,校验IP地址、Mac地址等其他信息 |
| | | */ |
| | | @Override |
| | | protected synchronized LicenseContent verify(final LicenseNotary notary) |
| | | throws Exception { |
| | | GenericCertificate certificate = getCertificate(); |
| | | |
| | | // Load license key from preferences, |
| | | final byte[] key = getLicenseKey(); |
| | | if (null == key){ |
| | | throw new NoLicenseInstalledException(getLicenseParam().getSubject()); |
| | | } |
| | | |
| | | certificate = getPrivacyGuard().key2cert(key); |
| | | notary.verify(certificate); |
| | | final LicenseContent content = (LicenseContent)this.load(certificate.getEncoded()); |
| | | this.validate(content); |
| | | setCertificate(certificate); |
| | | |
| | | return content; |
| | | } |
| | | |
| | | /** |
| | | * 校验生成证书的参数信息 |
| | | */ |
| | | protected synchronized void validateCreate(final LicenseContent content) |
| | | throws LicenseContentException { |
| | | final LicenseParam param = getLicenseParam(); |
| | | |
| | | final Date now = new Date(); |
| | | final Date notBefore = content.getNotBefore(); |
| | | final Date notAfter = content.getNotAfter(); |
| | | if (null != notAfter && now.after(notAfter)){ |
| | | throw new LicenseContentException("证书失效时间不能早于当前时间"); |
| | | } |
| | | if (null != notBefore && null != notAfter && notAfter.before(notBefore)){ |
| | | throw new LicenseContentException("证书生效时间不能晚于证书失效时间"); |
| | | } |
| | | final String consumerType = content.getConsumerType(); |
| | | if (null == consumerType){ |
| | | throw new LicenseContentException("用户类型不能为空"); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 复写validate方法,增加IP地址、Mac地址等其他信息校验 |
| | | */ |
| | | @Override |
| | | protected synchronized void validate(final LicenseContent content) |
| | | throws LicenseContentException { |
| | | //1. 首先调用父类的validate方法 |
| | | super.validate(content); |
| | | |
| | | //2. 然后校验自定义的License参数 |
| | | //License中可被允许的参数信息 |
| | | LicenseCheck expectedCheckModel = (LicenseCheck) content.getExtra(); |
| | | //当前服务器真实的参数信息 |
| | | LicenseCheck serverCheckModel = getServerInfos(); |
| | | |
| | | if(expectedCheckModel != null && serverCheckModel != null){ |
| | | //校验IP地址 |
| | | if(!checkIpAddress(expectedCheckModel.getIpAddress(),serverCheckModel.getIpAddress())){ |
| | | throw new LicenseContentException("当前服务器的IP没在授权范围内"); |
| | | } |
| | | |
| | | //校验Mac地址 |
| | | if(!checkIpAddress(expectedCheckModel.getMacAddress(),serverCheckModel.getMacAddress())){ |
| | | throw new LicenseContentException("当前服务器的Mac地址没在授权范围内"); |
| | | } |
| | | |
| | | //校验主板序列号 |
| | | if(!checkSerial(expectedCheckModel.getMainBoardSerial(),serverCheckModel.getMainBoardSerial())){ |
| | | throw new LicenseContentException("当前服务器的主板序列号没在授权范围内"); |
| | | } |
| | | |
| | | //校验CPU序列号 |
| | | if(!checkSerial(expectedCheckModel.getCpuSerial(),serverCheckModel.getCpuSerial())){ |
| | | throw new LicenseContentException("当前服务器的CPU序列号没在授权范围内"); |
| | | } |
| | | }else{ |
| | | throw new LicenseContentException("不能获取服务器硬件信息"); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 重写XMLDecoder解析XML |
| | | */ |
| | | private Object load(String encoded){ |
| | | BufferedInputStream inputStream = null; |
| | | XMLDecoder decoder = null; |
| | | try { |
| | | inputStream = new BufferedInputStream(new ByteArrayInputStream(encoded.getBytes(XML_CHARSET))); |
| | | |
| | | decoder = new XMLDecoder(new BufferedInputStream(inputStream, DEFAULT_BUFSIZE),null,null); |
| | | |
| | | return decoder.readObject(); |
| | | } catch (UnsupportedEncodingException e) { |
| | | e.printStackTrace(); |
| | | } finally { |
| | | try { |
| | | if(decoder != null){ |
| | | decoder.close(); |
| | | } |
| | | if(inputStream != null){ |
| | | inputStream.close(); |
| | | } |
| | | } catch (Exception e) { |
| | | logger.error("XMLDecoder解析XML失败",e); |
| | | } |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 获取当前服务器需要额外校验的License参数 |
| | | */ |
| | | private LicenseCheck getServerInfos(){ |
| | | //操作系统类型 |
| | | String osName = System.getProperty("os.name").toLowerCase(); |
| | | AbstractServerInfos abstractServerInfos = null; |
| | | |
| | | //根据不同操作系统类型选择不同的数据获取方法 |
| | | if (osName.startsWith("windows")) { |
| | | abstractServerInfos = new WindowsServerInfos(); |
| | | } else if (osName.startsWith("linux")) { |
| | | abstractServerInfos = new LinuxServerInfos(); |
| | | }else{//其他服务器类型 |
| | | abstractServerInfos = new WindowsServerInfos(); |
| | | } |
| | | |
| | | return abstractServerInfos.getServerInfos(); |
| | | } |
| | | |
| | | /** |
| | | * 校验当前服务器的IP/Mac地址是否在可被允许的IP范围内<br/> |
| | | * 如果存在IP在可被允许的IP/Mac地址范围内,则返回true |
| | | */ |
| | | private boolean checkIpAddress(List<String> expectedList,List<String> serverList){ |
| | | if(expectedList != null && expectedList.size() > 0){ |
| | | if(serverList != null && serverList.size() > 0){ |
| | | for(String expected : expectedList){ |
| | | if (expected == null) { |
| | | continue; |
| | | } |
| | | |
| | | if(serverList.contains(expected.trim())){ |
| | | return true; |
| | | } |
| | | } |
| | | } |
| | | |
| | | return false; |
| | | }else { |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 校验当前服务器硬件(主板、CPU等)序列号是否在可允许范围内 |
| | | */ |
| | | private boolean checkSerial(String expectedSerial,String serverSerial){ |
| | | if(!Cools.isEmpty(expectedSerial)){ |
| | | if(!Cools.isEmpty(serverSerial)){ |
| | | if(expectedSerial.equals(serverSerial)){ |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | }else{ |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.zy.system.entity.license; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serializable; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 自定义需要校验的License参数 |
| | | */ |
| | | @Data |
| | | public class LicenseCheck implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 8600137500316662317L; |
| | | /** |
| | | * 可被允许的IP地址 |
| | | */ |
| | | private List<String> ipAddress; |
| | | |
| | | /** |
| | | * 可被允许的MAC地址 |
| | | */ |
| | | private List<String> macAddress; |
| | | |
| | | /** |
| | | * 可被允许的CPU序列号 |
| | | */ |
| | | private String cpuSerial; |
| | | |
| | | /** |
| | | * 可被允许的主板序列号 |
| | | */ |
| | | private String mainBoardSerial; |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return "LicenseCheckModel{" + |
| | | "ipAddress=" + ipAddress + |
| | | ", macAddress=" + macAddress + |
| | | ", cpuSerial='" + cpuSerial + '\'' + |
| | | ", mainBoardSerial='" + mainBoardSerial + '\'' + |
| | | '}'; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.zy.system.entity.license; |
| | | |
| | | import com.core.common.Cools; |
| | | import com.zy.system.entity.LicenseInfos; |
| | | import com.zy.system.service.LicenseInfosService; |
| | | import com.zy.system.timer.LicenseTimer; |
| | | import de.schlichtherle.license.LicenseContent; |
| | | import org.apache.logging.log4j.LogManager; |
| | | import org.apache.logging.log4j.Logger; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.context.ApplicationContext; |
| | | import org.springframework.context.ApplicationListener; |
| | | import org.springframework.context.event.ContextRefreshedEvent; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.io.File; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * 在项目启动时安装证书 |
| | | */ |
| | | @Component |
| | | public class LicenseCheckListener implements ApplicationListener<ContextRefreshedEvent> { |
| | | private static Logger logger = LogManager.getLogger(LicenseCheckListener.class); |
| | | |
| | | /** |
| | | * 证书subject |
| | | */ |
| | | @Value("${license.subject}") |
| | | private String subject; |
| | | |
| | | /** |
| | | * 公钥别称 |
| | | */ |
| | | @Value("${license.publicAlias}") |
| | | private String publicAlias; |
| | | |
| | | /** |
| | | * 访问公钥库的密码 |
| | | */ |
| | | @Value("${license.storePass}") |
| | | private String storePass; |
| | | |
| | | /** |
| | | * 证书生成路径 |
| | | */ |
| | | @Value("${license.licensePath}") |
| | | private String licensePath; |
| | | |
| | | /** |
| | | * 密钥库存储路径 |
| | | */ |
| | | @Value("${license.publicKeysStorePath}") |
| | | private String publicKeysStorePath; |
| | | @Autowired |
| | | private LicenseTimer licenseTimer; |
| | | @Autowired |
| | | private LicenseInfosService licenseInfosService; |
| | | |
| | | @Override |
| | | public void onApplicationEvent(ContextRefreshedEvent event) { |
| | | //root application context 没有parent |
| | | ApplicationContext context = event.getApplicationContext().getParent(); |
| | | if(context == null){ |
| | | loadLicense(); |
| | | } |
| | | } |
| | | |
| | | //加载证书 |
| | | public boolean loadLicense() { |
| | | if(!Cools.isEmpty(licensePath)){ |
| | | logger.info("++++++++ 开始加载许可证 ++++++++"); |
| | | |
| | | try { |
| | | licenseTimer.getRemoteLicense(); |
| | | } catch (Exception e) { |
| | | } |
| | | |
| | | try { |
| | | LicenseVerifyParam param = new LicenseVerifyParam(); |
| | | param.setSubject(subject); |
| | | param.setPublicAlias(publicAlias); |
| | | param.setStorePass(storePass); |
| | | param.setLicensePath(licensePath); |
| | | param.setPublicKeysStorePath(publicKeysStorePath); |
| | | |
| | | LicenseVerify licenseVerify = new LicenseVerify(); |
| | | |
| | | LicenseInfos latestLicense = licenseInfosService.getLatestLicense(); |
| | | if (latestLicense == null) { |
| | | logger.info("许可证不存在"); |
| | | return false; |
| | | } |
| | | |
| | | //安装证书 |
| | | LicenseContent install = licenseVerify.install(param, latestLicense.getLicense()); |
| | | |
| | | logger.info("++++++++ 许可证加载结束 ++++++++"); |
| | | |
| | | licenseTimer.setSystemSupport(install!=null); |
| | | |
| | | if (install != null) { |
| | | Date start = new Date(); |
| | | Date end = install.getNotAfter(); |
| | | Long starTime = start.getTime(); |
| | | Long endTime = end.getTime(); |
| | | Long num = endTime - starTime;//时间戳相差的毫秒数 |
| | | int day = (int) (num / 24 / 60 / 60 / 1000); |
| | | licenseTimer.setLicenseDays(day); |
| | | } |
| | | |
| | | |
| | | return install != null; |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | return false; |
| | | } |
| | | } |
| | | licenseTimer.setSystemSupport(false); |
| | | return false; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.system.entity.license; |
| | | |
| | | import de.schlichtherle.license.*; |
| | | import org.apache.logging.log4j.LogManager; |
| | | import org.apache.logging.log4j.Logger; |
| | | |
| | | import javax.security.auth.x500.X500Principal; |
| | | import java.io.File; |
| | | import java.text.MessageFormat; |
| | | import java.util.prefs.Preferences; |
| | | |
| | | /** |
| | | * License生成类 |
| | | */ |
| | | public class LicenseCreator { |
| | | private static Logger logger = LogManager.getLogger(LicenseCreator.class); |
| | | private final static X500Principal DEFAULT_HOLDER_AND_ISSUER = new X500Principal("CN=localhost, OU=localhost, O=localhost, L=SH, ST=SH, C=CN"); |
| | | private LicenseCreatorParam param; |
| | | |
| | | public LicenseCreator(LicenseCreatorParam param) { |
| | | this.param = param; |
| | | } |
| | | |
| | | /** |
| | | * 生成License证书 |
| | | */ |
| | | public boolean generateLicense(){ |
| | | try { |
| | | LicenseManager licenseManager = new CustomLicenseManager(initLicenseParam()); |
| | | LicenseContent licenseContent = initLicenseContent(); |
| | | |
| | | licenseManager.store(licenseContent,new File(param.getLicensePath())); |
| | | |
| | | return true; |
| | | }catch (Exception e){ |
| | | logger.error(MessageFormat.format("证书生成失败:{0}",param),e); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 初始化证书生成参数 |
| | | */ |
| | | private LicenseParam initLicenseParam(){ |
| | | Preferences preferences = Preferences.userNodeForPackage(LicenseCreator.class); |
| | | |
| | | //设置对证书内容加密的秘钥 |
| | | CipherParam cipherParam = new DefaultCipherParam(param.getStorePass()); |
| | | |
| | | KeyStoreParam privateStoreParam = new CustomKeyStoreParam(LicenseCreator.class |
| | | ,param.getPrivateKeysStorePath() |
| | | ,param.getPrivateAlias() |
| | | ,param.getStorePass() |
| | | ,param.getKeyPass()); |
| | | |
| | | LicenseParam licenseParam = new DefaultLicenseParam(param.getSubject() |
| | | ,preferences |
| | | ,privateStoreParam |
| | | ,cipherParam); |
| | | |
| | | return licenseParam; |
| | | } |
| | | |
| | | /** |
| | | * 设置证书生成正文信息 |
| | | */ |
| | | private LicenseContent initLicenseContent(){ |
| | | LicenseContent licenseContent = new LicenseContent(); |
| | | licenseContent.setHolder(DEFAULT_HOLDER_AND_ISSUER); |
| | | licenseContent.setIssuer(DEFAULT_HOLDER_AND_ISSUER); |
| | | |
| | | licenseContent.setSubject(param.getSubject()); |
| | | licenseContent.setIssued(param.getIssuedTime()); |
| | | licenseContent.setNotBefore(param.getIssuedTime()); |
| | | licenseContent.setNotAfter(param.getExpiryTime()); |
| | | licenseContent.setConsumerType(param.getConsumerType()); |
| | | licenseContent.setConsumerAmount(param.getConsumerAmount()); |
| | | licenseContent.setInfo(param.getDescription()); |
| | | |
| | | //扩展校验服务器硬件信息 |
| | | licenseContent.setExtra(param.getLicenseCheck()); |
| | | |
| | | return licenseContent; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.zy.system.entity.license; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serializable; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * License生成类需要的参数 |
| | | */ |
| | | @Data |
| | | public class LicenseCreatorParam implements Serializable { |
| | | |
| | | private static final long serialVersionUID = -7793154252684580872L; |
| | | /** |
| | | * 证书subject |
| | | */ |
| | | private String subject; |
| | | |
| | | /** |
| | | * 密钥别称 |
| | | */ |
| | | private String privateAlias; |
| | | |
| | | /** |
| | | * 密钥密码(需要妥善保管,不能让使用者知道) |
| | | */ |
| | | private String keyPass; |
| | | |
| | | /** |
| | | * 访问秘钥库的密码 |
| | | */ |
| | | private String storePass; |
| | | |
| | | /** |
| | | * 证书生成路径 |
| | | */ |
| | | private String licensePath; |
| | | |
| | | /** |
| | | * 密钥库存储路径 |
| | | */ |
| | | private String privateKeysStorePath; |
| | | |
| | | /** |
| | | * 证书生效时间 |
| | | */ |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
| | | private Date issuedTime = new Date(); |
| | | |
| | | /** |
| | | * 证书失效时间 |
| | | */ |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
| | | private Date expiryTime; |
| | | |
| | | /** |
| | | * 用户类型 |
| | | */ |
| | | private String consumerType = "user"; |
| | | |
| | | /** |
| | | * 用户数量 |
| | | */ |
| | | private Integer consumerAmount = 1; |
| | | |
| | | /** |
| | | * 描述信息 |
| | | */ |
| | | private String description = ""; |
| | | |
| | | /** |
| | | * 额外的服务器硬件校验信息 |
| | | */ |
| | | private LicenseCheck licenseCheck; |
| | | |
| | | @Override |
| | | public String toString() { |
| | | return "LicenseCreatorParam{" + |
| | | "subject='" + subject + '\'' + |
| | | ", privateAlias='" + privateAlias + '\'' + |
| | | ", keyPass='" + keyPass + '\'' + |
| | | ", storePass='" + storePass + '\'' + |
| | | ", licensePath='" + licensePath + '\'' + |
| | | ", privateKeysStorePath='" + privateKeysStorePath + '\'' + |
| | | ", issuedTime=" + issuedTime + |
| | | ", expiryTime=" + expiryTime + |
| | | ", consumerType='" + consumerType + '\'' + |
| | | ", consumerAmount=" + consumerAmount + |
| | | ", description='" + description + '\'' + |
| | | ", licenseCheck=" + licenseCheck + |
| | | '}'; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.zy.system.entity.license; |
| | | |
| | | import de.schlichtherle.license.LicenseManager; |
| | | import de.schlichtherle.license.LicenseParam; |
| | | |
| | | public class LicenseManagerHolder { |
| | | |
| | | private static volatile LicenseManager LICENSE_MANAGER; |
| | | |
| | | public static LicenseManager getInstance(LicenseParam param) { |
| | | if (LICENSE_MANAGER == null) { |
| | | synchronized (LicenseManagerHolder.class) { |
| | | if (LICENSE_MANAGER == null) { |
| | | LICENSE_MANAGER = new CustomLicenseManager(param); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return LICENSE_MANAGER; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.zy.system.entity.license; |
| | | |
| | | import de.schlichtherle.license.*; |
| | | import org.apache.logging.log4j.LogManager; |
| | | import org.apache.logging.log4j.Logger; |
| | | |
| | | import java.io.File; |
| | | import java.io.IOException; |
| | | import java.nio.file.Files; |
| | | import java.nio.file.Path; |
| | | import java.text.DateFormat; |
| | | import java.text.MessageFormat; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Base64; |
| | | import java.util.prefs.Preferences; |
| | | |
| | | /** |
| | | * License校验类 |
| | | */ |
| | | public class LicenseVerify { |
| | | private static Logger logger = LogManager.getLogger(LicenseVerify.class); |
| | | |
| | | /** |
| | | * 安装License证书 |
| | | */ |
| | | public synchronized LicenseContent install(LicenseVerifyParam param, String license) { |
| | | LicenseContent result = null; |
| | | DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | //1. 安装证书 |
| | | try { |
| | | LicenseParam licenseParam = initLicenseParam(param); |
| | | LicenseManager licenseManager = LicenseManagerHolder.getInstance(licenseParam); |
| | | licenseManager.uninstall(); |
| | | |
| | | File tempFileFromBase64 = createTempFileFromBase64(license); |
| | | result = licenseManager.install(tempFileFromBase64); |
| | | logger.info(MessageFormat.format("许可证加载成功,许可证有效期:{0} - {1}", format.format(result.getNotBefore()), format.format(result.getNotAfter()))); |
| | | } catch (Exception e) { |
| | | logger.error("许可证加载失败!", e); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 校验License证书 |
| | | */ |
| | | public boolean verify(){ |
| | | try { |
| | | LicenseManager licenseManager = LicenseManagerHolder.getInstance(null); |
| | | DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | LicenseContent licenseContent = licenseManager.verify(); |
| | | logger.info(MessageFormat.format("许可证校验通过,许可证有效期:{0} - {1}",format.format(licenseContent.getNotBefore()),format.format(licenseContent.getNotAfter()))); |
| | | return true; |
| | | }catch (Exception e){ |
| | | logger.error("许可证校验失败!",e); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 校验License证书并获取证书信息 |
| | | */ |
| | | public LicenseContent getVerifyInfo(){ |
| | | LicenseManager licenseManager = LicenseManagerHolder.getInstance(null); |
| | | |
| | | //校验证书 |
| | | try { |
| | | LicenseContent licenseContent = licenseManager.verify(); |
| | | return licenseContent; |
| | | }catch (Exception e){ |
| | | logger.error("许可证校验失败!",e); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 初始化证书生成参数 |
| | | * @param param License校验类需要的参数 |
| | | * @return de.schlichtherle.license.LicenseParam |
| | | */ |
| | | private LicenseParam initLicenseParam(LicenseVerifyParam param){ |
| | | Preferences preferences = Preferences.userNodeForPackage(LicenseVerify.class); |
| | | |
| | | CipherParam cipherParam = new DefaultCipherParam(param.getStorePass()); |
| | | |
| | | KeyStoreParam publicStoreParam = new CustomKeyStoreParam(LicenseVerify.class |
| | | ,param.getPublicKeysStorePath() |
| | | ,param.getPublicAlias() |
| | | ,param.getStorePass() |
| | | ,null); |
| | | |
| | | return new DefaultLicenseParam(param.getSubject() |
| | | ,preferences |
| | | ,publicStoreParam |
| | | ,cipherParam); |
| | | } |
| | | |
| | | /** |
| | | * 将Base64字符串转换为临时文件 |
| | | * @param base64String Base64编码的字符串 |
| | | * @param filePrefix 文件名前缀(例如 "license_") |
| | | * @param fileSuffix 文件后缀(例如 ".lic") |
| | | * @return 生成的临时File对象(自动在JVM退出时删除) |
| | | * @throws IOException |
| | | */ |
| | | public File base64ToTempFile(String base64String, String filePrefix, String fileSuffix) |
| | | throws IOException { |
| | | // 解码Base64 |
| | | byte[] decodedBytes = Base64.getDecoder().decode(base64String); |
| | | // 创建临时文件 |
| | | Path tempPath = Files.createTempFile(filePrefix, fileSuffix); |
| | | // 写入内容 |
| | | Files.write(tempPath, decodedBytes); |
| | | // 设置JVM退出时自动删除 |
| | | tempPath.toFile().deleteOnExit(); |
| | | return tempPath.toFile(); |
| | | } |
| | | |
| | | public File createTempFileFromBase64(String base64Data) throws IOException { |
| | | return base64ToTempFile(base64Data, "temp_license_", ".bin"); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.zy.system.entity.license; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * 校验签名文件 |
| | | */ |
| | | @Data |
| | | public class LicenseVerifyParam { |
| | | |
| | | /** |
| | | * 证书subject |
| | | */ |
| | | private String subject; |
| | | |
| | | /** |
| | | * 公钥别称 |
| | | */ |
| | | private String publicAlias; |
| | | |
| | | /** |
| | | * 访问公钥库的密码 |
| | | */ |
| | | private String storePass; |
| | | |
| | | /** |
| | | * 证书生成路径 |
| | | */ |
| | | private String licensePath; |
| | | |
| | | /** |
| | | * 密钥库存储路径 |
| | | */ |
| | | private String publicKeysStorePath; |
| | | |
| | | public LicenseVerifyParam() { |
| | | |
| | | } |
| | | |
| | | public LicenseVerifyParam(String subject, String publicAlias, String storePass, String licensePath, String publicKeysStorePath) { |
| | | this.subject = subject; |
| | | this.publicAlias = publicAlias; |
| | | this.storePass = storePass; |
| | | this.licensePath = licensePath; |
| | | this.publicKeysStorePath = publicKeysStorePath; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.zy.system.entity.license; |
| | | |
| | | import com.core.common.Cools; |
| | | |
| | | import java.io.BufferedReader; |
| | | import java.io.InputStreamReader; |
| | | import java.net.InetAddress; |
| | | import java.util.List; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * 用于获取客户Linux服务器的基本信息 |
| | | */ |
| | | public class LinuxServerInfos extends AbstractServerInfos { |
| | | |
| | | @Override |
| | | protected List<String> getIpAddress() throws Exception { |
| | | List<String> result = null; |
| | | |
| | | //获取所有网络接口 |
| | | List<InetAddress> inetAddresses = getLocalAllInetAddress(); |
| | | |
| | | if (inetAddresses != null && inetAddresses.size() > 0) { |
| | | result = inetAddresses.stream().map(InetAddress::getHostAddress).distinct().map(String::toLowerCase).collect(Collectors.toList()); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | protected List<String> getMacAddress() throws Exception { |
| | | List<String> result = null; |
| | | |
| | | //1. 获取所有网络接口 |
| | | List<InetAddress> inetAddresses = getLocalAllInetAddress(); |
| | | |
| | | if (inetAddresses != null && inetAddresses.size() > 0) { |
| | | //2. 获取所有网络接口的Mac地址 |
| | | result = inetAddresses.stream().map(this::getMacByInetAddress).distinct().collect(Collectors.toList()); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | protected String getCPUSerial() throws Exception { |
| | | //序列号 |
| | | String serialNumber = ""; |
| | | |
| | | //使用dmidecode命令获取CPU序列号 |
| | | String[] shell = {"/bin/bash", "-c", "dmidecode -t processor | grep 'ID' | awk -F ':' '{print $2}' | head -n 1"}; |
| | | Process process = Runtime.getRuntime().exec(shell); |
| | | process.getOutputStream().close(); |
| | | |
| | | BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); |
| | | |
| | | if (null == reader.readLine()) { |
| | | return serialNumber; |
| | | } |
| | | String line = reader.readLine().trim(); |
| | | if (!Cools.isEmpty(line)) { |
| | | serialNumber = line; |
| | | } |
| | | |
| | | reader.close(); |
| | | return serialNumber; |
| | | } |
| | | |
| | | @Override |
| | | protected String getMainBoardSerial() throws Exception { |
| | | //序列号 |
| | | String serialNumber = ""; |
| | | |
| | | //使用dmidecode命令获取主板序列号 |
| | | String[] shell = {"/bin/bash", "-c", "dmidecode | grep 'Serial Number' | awk -F ':' '{print $2}' | head -n 1"}; |
| | | Process process = Runtime.getRuntime().exec(shell); |
| | | process.getOutputStream().close(); |
| | | |
| | | BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); |
| | | if (null == reader.readLine()) { |
| | | return serialNumber; |
| | | } |
| | | String line = reader.readLine().trim(); |
| | | if (!Cools.isEmpty(line)) { |
| | | serialNumber = line; |
| | | } |
| | | |
| | | reader.close(); |
| | | return serialNumber; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.system.entity.license; |
| | | |
| | | import java.net.InetAddress; |
| | | import java.util.List; |
| | | import java.util.Scanner; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * 用于获取客户Windows服务器的基本信息 |
| | | */ |
| | | public class WindowsServerInfos extends AbstractServerInfos { |
| | | |
| | | @Override |
| | | protected List<String> getIpAddress() throws Exception { |
| | | List<String> result = null; |
| | | |
| | | //获取所有网络接口 |
| | | List<InetAddress> inetAddresses = getLocalAllInetAddress(); |
| | | |
| | | if(inetAddresses != null && inetAddresses.size() > 0){ |
| | | result = inetAddresses.stream().map(InetAddress::getHostAddress).distinct().map(String::toLowerCase).collect(Collectors.toList()); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | protected List<String> getMacAddress() throws Exception { |
| | | List<String> result = null; |
| | | |
| | | //1. 获取所有网络接口 |
| | | List<InetAddress> inetAddresses = getLocalAllInetAddress(); |
| | | |
| | | if(inetAddresses != null && inetAddresses.size() > 0){ |
| | | //2. 获取所有网络接口的Mac地址 |
| | | result = inetAddresses.stream().map(this::getMacByInetAddress).distinct().collect(Collectors.toList()); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | protected String getCPUSerial() throws Exception { |
| | | //序列号 |
| | | String serialNumber = ""; |
| | | |
| | | //使用WMIC获取CPU序列号 |
| | | Process process = Runtime.getRuntime().exec("wmic cpu get processorid"); |
| | | process.getOutputStream().close(); |
| | | Scanner scanner = new Scanner(process.getInputStream()); |
| | | |
| | | if(scanner.hasNext()){ |
| | | scanner.next(); |
| | | } |
| | | |
| | | if(scanner.hasNext()){ |
| | | serialNumber = scanner.next().trim(); |
| | | } |
| | | |
| | | scanner.close(); |
| | | return serialNumber; |
| | | } |
| | | |
| | | @Override |
| | | protected String getMainBoardSerial() throws Exception { |
| | | //序列号 |
| | | String serialNumber = ""; |
| | | |
| | | //使用WMIC获取主板序列号 |
| | | Process process = Runtime.getRuntime().exec("wmic baseboard get serialnumber"); |
| | | process.getOutputStream().close(); |
| | | Scanner scanner = new Scanner(process.getInputStream()); |
| | | |
| | | if(scanner.hasNext()){ |
| | | scanner.next(); |
| | | } |
| | | |
| | | if(scanner.hasNext()){ |
| | | serialNumber = scanner.next().trim(); |
| | | } |
| | | |
| | | scanner.close(); |
| | | return serialNumber; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.system.mapper; |
| | | |
| | | import com.zy.system.entity.LicenseInfos; |
| | | import com.baomidou.mybatisplus.mapper.BaseMapper; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.springframework.stereotype.Repository; |
| | | |
| | | @Mapper |
| | | @Repository |
| | | public interface LicenseInfosMapper extends BaseMapper<LicenseInfos> { |
| | | |
| | | LicenseInfos getLatestLicense(); |
| | | |
| | | } |
| New file |
| | |
| | | package com.zy.system.service; |
| | | |
| | | import com.zy.system.entity.LicenseInfos; |
| | | import com.baomidou.mybatisplus.service.IService; |
| | | |
| | | public interface LicenseInfosService extends IService<LicenseInfos> { |
| | | |
| | | LicenseInfos getLatestLicense(); |
| | | |
| | | } |
| New file |
| | |
| | | package com.zy.system.service.impl; |
| | | |
| | | import com.zy.system.mapper.LicenseInfosMapper; |
| | | import com.zy.system.entity.LicenseInfos; |
| | | import com.zy.system.service.LicenseInfosService; |
| | | import com.baomidou.mybatisplus.service.impl.ServiceImpl; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | @Service("licenseInfosService") |
| | | public class LicenseInfosServiceImpl extends ServiceImpl<LicenseInfosMapper, LicenseInfos> implements LicenseInfosService { |
| | | |
| | | @Override |
| | | public LicenseInfos getLatestLicense() { |
| | | return this.baseMapper.getLatestLicense(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.zy.system.timer; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.zy.common.utils.HttpHandler; |
| | | import com.zy.system.entity.LicenseInfos; |
| | | import com.zy.system.entity.license.*; |
| | | import com.zy.system.service.LicenseInfosService; |
| | | import de.schlichtherle.license.LicenseContent; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import java.util.Date; |
| | | import java.util.HashMap; |
| | | |
| | | @Component |
| | | public class LicenseTimer { |
| | | |
| | | private static boolean SYSTEM_SUPPORT = false;//系统激活状态,默认关闭 |
| | | |
| | | private static int LICENSE_DAYS = 0;//许可证天数 |
| | | |
| | | /** |
| | | * 证书subject |
| | | */ |
| | | @Value("${license.subject}") |
| | | private String subject; |
| | | |
| | | /** |
| | | * 公钥别称 |
| | | */ |
| | | @Value("${license.publicAlias}") |
| | | private String publicAlias; |
| | | |
| | | /** |
| | | * 访问公钥库的密码 |
| | | */ |
| | | @Value("${license.storePass}") |
| | | private String storePass; |
| | | |
| | | /** |
| | | * 证书生成路径 |
| | | */ |
| | | @Value("${license.licensePath}") |
| | | private String licensePath; |
| | | |
| | | /** |
| | | * 密钥库存储路径 |
| | | */ |
| | | @Value("${license.publicKeysStorePath}") |
| | | private String publicKeysStorePath; |
| | | |
| | | @Autowired |
| | | private LicenseInfosService licenseInfosService; |
| | | |
| | | //每天晚上11点更新系统激活状态 |
| | | @Scheduled(cron = "0 0 23 * * ? ") |
| | | public void timer() { |
| | | try { |
| | | getRemoteLicense(); |
| | | } catch (Exception e) { |
| | | |
| | | } |
| | | |
| | | try { |
| | | verify(); |
| | | } catch (Exception e) { |
| | | |
| | | } |
| | | } |
| | | |
| | | public void getRemoteLicense() { |
| | | try { |
| | | AbstractServerInfos abstractServerInfos = null; |
| | | String osName = System.getProperty("os.name"); |
| | | //根据不同操作系统类型选择不同的数据获取方法 |
| | | if (osName.startsWith("windows")) { |
| | | abstractServerInfos = new WindowsServerInfos(); |
| | | } else if (osName.startsWith("linux")) { |
| | | abstractServerInfos = new LinuxServerInfos(); |
| | | }else{//其他服务器类型 |
| | | abstractServerInfos = new WindowsServerInfos(); |
| | | } |
| | | LicenseCheck serverInfos = abstractServerInfos.getServerInfos(); |
| | | |
| | | HashMap<String, Object> map = new HashMap<>(); |
| | | map.put("subject", subject); |
| | | map.put("licenseCheck", serverInfos); |
| | | |
| | | String response = new HttpHandler.Builder() |
| | | .setUri("http://net.zoneyung.net:9999/license") |
| | | .setPath("/remoteQueryLicense") |
| | | .setJson(JSON.toJSONString(map)) |
| | | .build() |
| | | .doPost(); |
| | | JSONObject jsonObject = JSON.parseObject(response); |
| | | if (jsonObject.getString("result").equals("ok")) { |
| | | LicenseInfos licenseInfos = new LicenseInfos(); |
| | | licenseInfos.setLicense(jsonObject.getString("data")); |
| | | licenseInfos.setCreateTime(new Date()); |
| | | licenseInfos.setLicenseTime(jsonObject.getString("licenseTime")); |
| | | licenseInfosService.insert(licenseInfos); |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | public void verify() { |
| | | LicenseInfos latestLicense = licenseInfosService.getLatestLicense(); |
| | | if (latestLicense == null) { |
| | | setLicenseDays(0); |
| | | setSystemSupport(false); |
| | | return; |
| | | } |
| | | |
| | | LicenseVerifyParam param = new LicenseVerifyParam(); |
| | | param.setSubject(subject); |
| | | param.setPublicAlias(publicAlias); |
| | | param.setStorePass(storePass); |
| | | param.setLicensePath(licensePath); |
| | | param.setPublicKeysStorePath(publicKeysStorePath); |
| | | |
| | | //验证许可证是否有效 |
| | | LicenseVerify licenseVerify = new LicenseVerify(); |
| | | //安装证书 |
| | | LicenseContent install = licenseVerify.install(param, latestLicense.getLicense()); |
| | | |
| | | if (install != null) { |
| | | Date start = new Date(); |
| | | Date end = install.getNotAfter(); |
| | | Long starTime = start.getTime(); |
| | | Long endTime = end.getTime(); |
| | | long num = endTime - starTime;//时间戳相差的毫秒数 |
| | | int day = (int) (num / 24 / 60 / 60 / 1000); |
| | | setLicenseDays(day); |
| | | setSystemSupport(true); |
| | | }else { |
| | | setLicenseDays(0); |
| | | setSystemSupport(false); |
| | | } |
| | | } |
| | | |
| | | public boolean getSystemSupport() { |
| | | return SYSTEM_SUPPORT; |
| | | } |
| | | |
| | | public void setSystemSupport(boolean systemSupport) { |
| | | SYSTEM_SUPPORT = systemSupport; |
| | | } |
| | | |
| | | public int getLicenseDays() { |
| | | return LICENSE_DAYS; |
| | | } |
| | | |
| | | public void setLicenseDays(int licenseDays) { |
| | | LICENSE_DAYS = licenseDays; |
| | | } |
| | | |
| | | } |
| | |
| | | swagger: |
| | | enable: false |
| | | |
| | | #License相关配置 |
| | | license: |
| | | subject: crnWcsDev |
| | | publicAlias: publicCert |
| | | storePass: public_zhongyang_123456789 |
| | | licensePath: license.lic |
| | | publicKeysStorePath: publicCerts.keystore |
| | | |
| | | deviceExecuteConfig: |
| | | # 每个线程管控设备执行数量 |
| | | threadControlCount: 10 |
| | |
| | | <result column="control_rows" property="controlRows" /> |
| | | <result column="in_station_list" property="inStationList" /> |
| | | <result column="out_station_list" property="outStationList" /> |
| | | <result column="max_in_task" property="maxInTask" /> |
| | | <result column="max_out_task" property="maxOutTask" /> |
| | | |
| | | </resultMap> |
| | | |
| New file |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.zy.system.mapper.LicenseInfosMapper"> |
| | | |
| | | <!-- 通用查询映射结果 --> |
| | | <resultMap id="BaseResultMap" type="com.zy.system.entity.LicenseInfos"> |
| | | <id column="id" property="id" /> |
| | | <result column="license" property="license" /> |
| | | <result column="license_time" property="licenseTime" /> |
| | | <result column="create_time" property="createTime" /> |
| | | |
| | | </resultMap> |
| | | |
| | | <select id="getLatestLicense" resultMap="BaseResultMap"> |
| | | select * from sys_license_infos order by create_time desc limit 0,1 |
| | | </select> |
| | | |
| | | </mapper> |
| | |
| | | handler(newVal, oldVal) { |
| | | if (newVal.crnNo != 0) { |
| | | this.activeNames = newVal.crnNo; |
| | | this.searchCrnNo = newVal.crnNo; |
| | | const idx = this.crnList.findIndex(i => i.crnNo == newVal.crnNo); |
| | | if (idx >= 0) { this.currentPage = Math.floor(idx / this.pageSize) + 1; } |
| | | } |
| | |
| | | ,{field: 'controlRows', align: 'center',title: '控制库位排号'} |
| | | ,{field: 'inStationList', align: 'center',title: '入库站列表'} |
| | | ,{field: 'outStationList', align: 'center',title: '出库站列表'} |
| | | ,{field: 'maxInTask', align: 'center',title: '最大入库任务数'} |
| | | ,{field: 'maxOutTask', align: 'center',title: '最大出库任务数'} |
| | | // ,{field: 'createBy', align: 'center',title: '创建人员'} |
| | | // ,{field: 'createTime$', align: 'center',title: '创建时间'} |
| | | // ,{field: 'updateBy', align: 'center',title: '修改人员'} |
| New file |
| | |
| | | layui.use(['tree', 'layer', 'form', 'element'], function() { |
| | | var tree = layui.tree; |
| | | var $ = layui.jquery; |
| | | var layer = layui.layer; |
| | | var form = layui.form; |
| | | var element = layui.element; |
| | | |
| | | var currentDay = null; |
| | | |
| | | function buildMonthTree(data) { |
| | | var monthMap = {}; |
| | | (data || []).forEach(function (y) { |
| | | (y.children || []).forEach(function (m) { |
| | | var month = m.title; |
| | | var arr = monthMap[month] || (monthMap[month] = []); |
| | | (m.children || []).forEach(function (d) { |
| | | arr.push({ title: d.title, id: d.id }); |
| | | }); |
| | | }); |
| | | }); |
| | | var result = []; |
| | | Object.keys(monthMap).sort().forEach(function (month) { |
| | | result.push({ title: month + '月', id: month, children: monthMap[month] }); |
| | | }); |
| | | return result; |
| | | } |
| | | |
| | | function loadDateTree() { |
| | | $.ajax({ |
| | | url: baseUrl + "/deviceLog/dates/auth", |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | method: 'GET', |
| | | beforeSend: function () { |
| | | layer.load(1, {shade: [0.1,'#fff']}); |
| | | }, |
| | | success: function (res) { |
| | | layer.closeAll('loading'); |
| | | if (res.code === 200) { |
| | | var monthTree = buildMonthTree(res.data); |
| | | tree.render({ |
| | | elem: '#date-tree', |
| | | id: 'dateTree', |
| | | data: monthTree, |
| | | click: function(obj){ |
| | | var node = obj.data; |
| | | if (node.id && node.id.length === 8) { |
| | | currentDay = node.id; |
| | | $('#selected-day').val(currentDay); |
| | | loadDevices(currentDay); |
| | | } |
| | | } |
| | | }); |
| | | } else if (res.code === 403) { |
| | | top.location.href = baseUrl + "/"; |
| | | } else { |
| | | layer.msg(res.msg || '加载日期失败', {icon: 2}); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | |
| | | function loadDevices(day) { |
| | | $('#device-list').html(''); |
| | | $.ajax({ |
| | | url: baseUrl + "/deviceLog/day/" + day + "/devices/auth", |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | method: 'GET', |
| | | beforeSend: function () { |
| | | layer.load(1, {shade: [0.1,'#fff']}); |
| | | }, |
| | | success: function (res) { |
| | | layer.closeAll('loading'); |
| | | if (res.code === 200) { |
| | | if (!res.data || res.data.length === 0) { |
| | | $('#device-list').html('<div class="layui-text">当日未找到设备日志</div>'); |
| | | return; |
| | | } |
| | | var html = ''; |
| | | res.data.forEach(function(item){ |
| | | var types = item.types || []; |
| | | var typeBtns = ''; |
| | | types.forEach(function(t){ |
| | | typeBtns += '<button class="layui-btn layui-btn-xs" data-type="' + t + '" data-device-no="' + item.deviceNo + '">下载(' + t + ')</button>'; |
| | | }); |
| | | html += '<div class="layui-col-xs12" style="margin-bottom:8px;">' + |
| | | '<div class="layui-card">' + |
| | | '<div class="layui-card-body">' + |
| | | '<span>设备编号:<b>' + item.deviceNo + '</b></span>' + |
| | | '<span style="margin-left:20px;">类型:' + types.join(',') + '</span>' + |
| | | '<span style="margin-left:20px;">文件数:' + item.fileCount + '</span>' + |
| | | '<span style="margin-left:20px;">' + typeBtns + '</span>' + |
| | | '</div>' + |
| | | '</div>' + |
| | | '</div>'; |
| | | }); |
| | | $('#device-list').html(html); |
| | | } else if (res.code === 403) { |
| | | top.location.href = baseUrl + "/"; |
| | | } else { |
| | | layer.msg(res.msg || '加载设备失败', {icon: 2}); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | |
| | | function downloadDeviceLog(day, type, deviceNo) { |
| | | if (!day) { |
| | | layer.msg('请先选择日期', {icon: 2}); |
| | | return; |
| | | } |
| | | if (!type) { |
| | | layer.msg('请选择设备类型', {icon: 2}); |
| | | return; |
| | | } |
| | | if (!deviceNo) { |
| | | layer.msg('请输入设备编号', {icon: 2}); |
| | | return; |
| | | } |
| | | var offsetVal = parseInt($('#file-offset').val()); |
| | | var limitVal = parseInt($('#file-limit').val()); |
| | | var offset = isNaN(offsetVal) || offsetVal < 0 ? 0 : offsetVal; |
| | | var limit = isNaN(limitVal) || limitVal <= 0 ? 200 : limitVal; |
| | | $.ajax({ |
| | | url: baseUrl + "/deviceLog/download/init/auth", |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | method: 'POST', |
| | | data: JSON.stringify({ day: day, type: type, deviceNo: deviceNo, offset: offset, limit: limit }), |
| | | dataType:'json', |
| | | contentType:'application/json;charset=UTF-8', |
| | | success: function (res) { |
| | | if (res.code !== 200) { |
| | | layer.msg(res.msg || '初始化失败', {icon: 2}); |
| | | return; |
| | | } |
| | | var pid = res.data.progressId; |
| | | var progressIndex = layer.open({ |
| | | type: 1, |
| | | title: '下载中', |
| | | area: ['520px', '200px'], |
| | | content: '<div style="padding:16px;">' + |
| | | '<div class="layui-text" style="margin-bottom:15px;">压缩生成进度</div>' + |
| | | '<div class="layui-progress" lay-showPercent="true" lay-filter="buildProgress">' + |
| | | '<div class="layui-progress-bar" style="width:0%"><span class="layui-progress-text">0%</span></div>' + |
| | | '</div>' + |
| | | '<div class="layui-text" style="margin:12px 0 15px;">下载接收进度</div>' + |
| | | '<div class="layui-progress" lay-showPercent="true" lay-filter="receiveProgress">' + |
| | | '<div class="layui-progress-bar" style="width:0%"><span class="layui-progress-text">0%</span></div>' + |
| | | '</div>' + |
| | | '</div>' |
| | | }); |
| | | var timer = setInterval(function(){ |
| | | $.ajax({ |
| | | url: baseUrl + '/deviceLog/download/progress/auth', |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | method: 'GET', |
| | | data: { id: pid }, |
| | | success: function (p) { |
| | | if (p.code === 200) { |
| | | var percent = p.data.percent || 0; |
| | | element.progress('buildProgress', percent + '%'); |
| | | // 隐藏实时大小,不更新文字 |
| | | } |
| | | } |
| | | }); |
| | | }, 500); |
| | | |
| | | $.ajax({ |
| | | url: baseUrl + "/deviceLog/day/" + day + "/download/auth?type=" + encodeURIComponent(type) + "&deviceNo=" + encodeURIComponent(deviceNo) + "&offset=" + offset + "&limit=" + limit + "&progressId=" + encodeURIComponent(pid), |
| | | headers: {'token': localStorage.getItem('token')}, |
| | | method: 'GET', |
| | | xhrFields: { responseType: 'blob' }, |
| | | xhr: function(){ |
| | | var xhr = new window.XMLHttpRequest(); |
| | | xhr.onprogress = function(e){ |
| | | var percent = 0; |
| | | if (e.lengthComputable && e.total > 0) { |
| | | percent = Math.floor(e.loaded / e.total * 100); |
| | | element.progress('receiveProgress', percent + '%'); |
| | | } |
| | | // 隐藏实时大小,不更新文字 |
| | | }; |
| | | return xhr; |
| | | }, |
| | | success: function (data, status, xhr) { |
| | | var disposition = xhr.getResponseHeader('Content-Disposition') || ''; |
| | | var filename = type + '_' + deviceNo + '_' + day + '.zip'; |
| | | var match = /filename=(.+)/.exec(disposition); |
| | | if (match && match[1]) { |
| | | filename = decodeURIComponent(match[1]); |
| | | } |
| | | element.progress('buildProgress', '100%'); |
| | | element.progress('receiveProgress', '100%'); |
| | | var blob = new Blob([data], {type: 'application/zip'}); |
| | | var link = document.createElement('a'); |
| | | var url = window.URL.createObjectURL(blob); |
| | | link.href = url; |
| | | link.download = filename; |
| | | document.body.appendChild(link); |
| | | link.click(); |
| | | document.body.removeChild(link); |
| | | window.URL.revokeObjectURL(url); |
| | | clearInterval(timer); |
| | | setTimeout(function(){ layer.close(progressIndex); }, 300); |
| | | }, |
| | | error: function () { |
| | | clearInterval(timer); |
| | | layer.close(progressIndex); |
| | | layer.msg('下载失败或未找到日志', {icon: 2}); |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | $(document).on('click', '#download-btn', function () { |
| | | downloadDeviceLog(currentDay, $('#device-type-input').val(), $('#device-no-input').val()); |
| | | }); |
| | | |
| | | $(document).on('click', '#device-list .layui-btn', function () { |
| | | var deviceNo = $(this).attr('data-device-no'); |
| | | var type = $(this).attr('data-type'); |
| | | downloadDeviceLog(currentDay, type, deviceNo); |
| | | }); |
| | | |
| | | loadDateTree(); |
| | | limit(); |
| | | }); |
| | | |
| | |
| | | </div> |
| | | </div> |
| | | <div class="layui-form-item"> |
| | | <label class="layui-form-label">最大入库任务数: </label> |
| | | <div class="layui-input-block"> |
| | | <input class="layui-input" name="maxInTask" placeholder="请输入最大入库任务数"> |
| | | </div> |
| | | </div> |
| | | <div class="layui-form-item"> |
| | | <label class="layui-form-label">最大出库任务数: </label> |
| | | <div class="layui-input-block"> |
| | | <input class="layui-input" name="maxOutTask" placeholder="请输入最大出库任务数"> |
| | | </div> |
| | | </div> |
| | | <div class="layui-form-item"> |
| | | <label class="layui-form-label">备注: </label> |
| | | <div class="layui-input-block"> |
| | | <input class="layui-input" name="memo" placeholder="请输入备注"> |
| New file |
| | |
| | | <!DOCTYPE html> |
| | | <html lang="zh-CN"> |
| | | <head> |
| | | <meta charset="utf-8"> |
| | | <title>设备日志</title> |
| | | <meta name="renderer" content="webkit"> |
| | | <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"> |
| | | <link rel="stylesheet" href="../../static/layui/css/layui.css" media="all"> |
| | | <link rel="stylesheet" href="../../static/css/admin.css?v=318" media="all"> |
| | | <link rel="stylesheet" href="../../static/css/cool.css" media="all"> |
| | | </head> |
| | | <body> |
| | | |
| | | <div class="layui-fluid"> |
| | | <div class="layui-row"> |
| | | <div class="layui-col-md3"> |
| | | <div class="layui-card"> |
| | | <div class="layui-card-header">日期</div> |
| | | <div class="layui-card-body"> |
| | | <div id="date-tree"></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="layui-col-md9"> |
| | | <div class="layui-card"> |
| | | <div class="layui-card-header">日志下载</div> |
| | | <div class="layui-card-body"> |
| | | <form class="layui-form toolbar" id="search-box"> |
| | | <div class="layui-form-item"> |
| | | <div class="layui-inline"> |
| | | <label class="layui-form-label">选中日期:</label> |
| | | <div class="layui-input-inline"> |
| | | <input id="selected-day" class="layui-input" type="text" placeholder="yyyyMMdd" readonly> |
| | | </div> |
| | | </div> |
| | | <div class="layui-inline"> |
| | | <label class="layui-form-label">设备类型:</label> |
| | | <div class="layui-input-inline"> |
| | | <select id="device-type-input" class="layui-input"> |
| | | <option value="">请选择</option> |
| | | <option value="Crn">Crn</option> |
| | | <option value="Devp">Devp</option> |
| | | <option value="Rgv">Rgv</option> |
| | | </select> |
| | | </div> |
| | | </div> |
| | | <div class="layui-inline"> |
| | | <label class="layui-form-label">设备编号:</label> |
| | | <div class="layui-input-inline"> |
| | | <input id="device-no-input" class="layui-input" type="text" placeholder="请输入设备编号"> |
| | | </div> |
| | | </div> |
| | | <div class="layui-inline"> |
| | | <label class="layui-form-label">起始序号:</label> |
| | | <div class="layui-input-inline"> |
| | | <input id="file-offset" class="layui-input" type="text" placeholder="默认0"> |
| | | </div> |
| | | </div> |
| | | <div class="layui-inline"> |
| | | <label class="layui-form-label">最大文件数:</label> |
| | | <div class="layui-input-inline"> |
| | | <input id="file-limit" class="layui-input" type="text" placeholder="默认200"> |
| | | </div> |
| | | </div> |
| | | <div class="layui-inline"> |
| | | <button id="download-btn" type="button" class="layui-btn layui-btn-normal">下载</button> |
| | | </div> |
| | | </div> |
| | | </form> |
| | | |
| | | <hr class="layui-bg-gray"> |
| | | |
| | | <div class="layui-row"> |
| | | <div class="layui-col-xs12"> |
| | | <div class="layui-card"> |
| | | <div class="layui-card-header">该日设备列表</div> |
| | | <div class="layui-card-body"> |
| | | <div id="device-list" class="layui-row"></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <script type="text/javascript" src="../../static/js/jquery/jquery-3.3.1.min.js"></script> |
| | | <script type="text/javascript" src="../../static/layui/layui.js" charset="utf-8"></script> |
| | | <script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script> |
| | | <script type="text/javascript" src="../../static/js/cool.js" charset="utf-8"></script> |
| | | <script type="text/javascript" src="../../static/js/deviceLogs/deviceLogs.js" charset="utf-8"></script> |
| | | </body> |
| | | </html> |
| | |
| | | <html lang="en"> |
| | | <head> |
| | | <meta charset="utf-8"> |
| | | <title>浙江中扬 - 自动化立体仓库 - AS / RS</title> |
| | | <title>浙江中扬 - 自动化立体仓库 - WCS</title> |
| | | <meta name="renderer" content="webkit"> |
| | | <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=0"> |
| | |
| | | font-size: 18px; |
| | | font-weight: 400; |
| | | /*margin-left: 5px;*/ |
| | | } |
| | | |
| | | /* 弹窗样式 */ |
| | | .popup { |
| | | position: fixed; |
| | | top: 0; |
| | | left: 0; |
| | | width: 100%; |
| | | height: 100%; |
| | | background-color: rgba(0,0,0,0.5); |
| | | display: none; |
| | | justify-content: center; |
| | | align-items: center; |
| | | z-index: 9999; |
| | | } |
| | | |
| | | .popup-content { |
| | | background-color: #fff; |
| | | padding: 20px; |
| | | border-radius: 5px; |
| | | box-shadow: 0px 0px 20px rgba(0,0,0,0.3); |
| | | text-align: center; |
| | | } |
| | | </style> |
| | | </head> |
| | |
| | | <!-- <li class="layui-nav-item" lay-unselect>--> |
| | | <!-- <a ew-event="note" title="便签"><i class="layui-icon layui-icon-note"></i></a>--> |
| | | <!-- </li>--> |
| | | <li class="layui-nav-item" lay-unselect id="fakeShow" style="display: none;user-select: none;"> |
| | | <li class="layui-nav-item" lay-unselect id="fakeShow" style="display: none;user-select: none;margin-right: 10px;"> |
| | | <div style="color: red;" id="fakeShowText">仿真模拟运行中</div> |
| | | </li> |
| | | <li class="layui-nav-item" lay-unselect id="licenseShow" style="display: none;user-select: none;"> |
| | | <div style="color: red;">许可证有效期:<span id="licenseDays">29</span>天</div> |
| | | <div style="color: red;">临时许可证有效期:<span id="licenseDays">29</span>天</div> |
| | | </li> |
| | | <li class="layui-nav-item layui-hide-xs" lay-unselect> |
| | | <a ew-event="fullScreen" title="全屏"><i class="layui-icon layui-icon-screen-full"></i></a> |
| | |
| | | <div class="layuimini-loader-inner"></div> |
| | | </div> |
| | | |
| | | <!-- 弹窗内容 --> |
| | | <div class="popup" id="popup"> |
| | | <div class="popup-content"> |
| | | <h2 style="font-size: 28px;margin-bottom: 10px;">许可证即将过期</h2> |
| | | <div id="popup-text" style="font-size: 28px;color: red"></div> |
| | | <button style="background-color: #007bff;color: #fff;border: none;padding: 10px 20px;border-radius: 5px;cursor: pointer;font-size: 16px;" onclick="hidePopup()">关闭</button> |
| | | </div> |
| | | </div> |
| | | |
| | | <script type="text/javascript" src="../static/js/jquery/jquery-3.3.1.min.js"></script> |
| | | <script type="text/javascript" src="../static/layui/layui.js"></script> |
| | | <script type="text/javascript" src="../static/js/handlebars/handlebars-v4.5.3.js"></script> |
| | |
| | | top.location.href = baseUrl + "/login"; |
| | | } |
| | | }); |
| | | |
| | | // 显示弹窗 |
| | | function showPopup(res) { |
| | | document.getElementById('popup').style.display = 'block'; |
| | | // 获取弹出窗口内容的容器元素 |
| | | var popupText = document.getElementById('popup-text'); |
| | | // 假设后台返回的字符串为 responseString |
| | | if (res!==""){ |
| | | // 获取当前日期 |
| | | const currentDate = new Date(); |
| | | // 创建新日期对象并添加天数 |
| | | const newDate = new Date(); |
| | | newDate.setDate(currentDate.getDate() + res + 1); |
| | | // 将字符串设置为弹窗内容的文本 |
| | | popupText.textContent = "许可证将于" + new Intl.DateTimeFormat('zh-CN').format(newDate) + "过期,剩余有效期:" + res + "天!"; |
| | | }else { |
| | | document.getElementById('popup').style.display = 'none'; |
| | | } |
| | | } |
| | | |
| | | // 隐藏弹窗 |
| | | function hidePopup() { |
| | | document.getElementById('popup').style.display = 'none'; |
| | | } |
| | | |
| | | layui.config({ |
| | | base: baseUrl + "/static/layui/lay/modules/" |
| | |
| | | $("#licenseShow").show() |
| | | $("#licenseDays").html(days) |
| | | } |
| | | |
| | | if (days <= 15) { |
| | | showPopup(days) |
| | | } |
| | | }else { |
| | | top.location.href = baseUrl + "/login"; |
| | | } |