调用mes导入工程参数修改,Excel表数据流程卡号一致;增加读取层号/工程号方法;
工程号一致覆盖更新
| | |
| | | private void extractField(S7ParseData item, Object data) { |
| | | switch (item.getDataType()) { |
| | | case BOOL: |
| | | item.setDataItem(DataItem.createReqByBoolean((Boolean) data)); |
| | | // 类型转换容错:支持 Integer/Number 转 Boolean |
| | | Boolean boolValue; |
| | | if (data instanceof Boolean) { |
| | | boolValue = (Boolean) data; |
| | | } else if (data instanceof Number) { |
| | | boolValue = ((Number) data).intValue() != 0; |
| | | } else if (data instanceof String) { |
| | | String str = ((String) data).trim().toLowerCase(); |
| | | boolValue = "true".equals(str) || "1".equals(str) || "on".equals(str); |
| | | } else { |
| | | // 尝试转换为数字再转Boolean |
| | | try { |
| | | int intValue = Integer.parseInt(String.valueOf(data)); |
| | | boolValue = intValue != 0; |
| | | } catch (NumberFormatException e) { |
| | | throw new S7CommException("无法将值转换为Boolean: " + data + " (类型: " + (data != null ? data.getClass().getName() : "null") + ")"); |
| | | } |
| | | } |
| | | item.setDataItem(DataItem.createReqByBoolean(boolValue)); |
| | | break; |
| | | case BYTE: |
| | | item.setDataItem(DataItem.createReqByByte(ByteReadBuff.newInstance((byte[]) data) |
| | |
| | | return Result.error("设备配置数据格式错误"); |
| | | } |
| | | |
| | | deviceConfig.setId(request.getDeviceId()); |
| | | deviceConfig.setId(request.getId()); |
| | | boolean success = deviceConfigService.updateDevice(deviceConfig); |
| | | if (success) { |
| | | // 更新成功后,重新获取设备对象 |
| | | DeviceConfig updated = deviceConfigService.getDeviceById(request.getDeviceId()); |
| | | DeviceConfig updated = deviceConfigService.getDeviceById(request.getId()); |
| | | return Result.success(updated); |
| | | } else { |
| | | return Result.error("设备配置不存在"); |
| | |
| | | public Result<Void> deleteDevice( |
| | | @Valid @RequestBody DeviceConfigRequest request) { |
| | | try { |
| | | deviceConfigService.deleteDevice(request.getDeviceId()); |
| | | deviceConfigService.deleteDevice(request.getId()); |
| | | return Result.success(null); |
| | | } catch (Exception e) { |
| | | log.error("删除设备配置失败", e); |
| | |
| | | public Result<DeviceConfig> getDeviceById( |
| | | @Valid @RequestBody DeviceConfigRequest request) { |
| | | try { |
| | | DeviceConfig device = deviceConfigService.getDeviceById(request.getDeviceId()); |
| | | DeviceConfig device = deviceConfigService.getDeviceById(request.getId()); |
| | | return Result.success(device); |
| | | } catch (Exception e) { |
| | | log.error("获取设备配置失败", e); |
| | |
| | | public Result<Void> enableDevice( |
| | | @Valid @RequestBody DeviceConfigRequest request) { |
| | | try { |
| | | deviceConfigService.enableDevice(request.getDeviceId()); |
| | | deviceConfigService.enableDevice(request.getId()); |
| | | return Result.success(null); |
| | | } catch (Exception e) { |
| | | log.error("启用设备失败", e); |
| | |
| | | public Result<Void> disableDevice( |
| | | @Valid @RequestBody DeviceConfigRequest request) { |
| | | try { |
| | | deviceConfigService.disableDevice(request.getDeviceId()); |
| | | deviceConfigService.disableDevice(request.getId()); |
| | | return Result.success(null); |
| | | } catch (Exception e) { |
| | | log.error("禁用设备失败", e); |
| | |
| | | public Result<Boolean> checkDeviceCodeExists( |
| | | @ApiParam("设备配置请求") @RequestBody DeviceConfigRequest request) { |
| | | try { |
| | | boolean exists = deviceConfigService.isDeviceCodeExists(request.getDeviceCode(), request.getDeviceId()); |
| | | boolean exists = deviceConfigService.isDeviceCodeExists(request.getDeviceCode(), request.getId()); |
| | | return Result.success(exists); |
| | | } catch (Exception e) { |
| | | log.error("检查设备编码失败", e); |
| | |
| | | public Result<DeviceConfigVO.HealthCheckResult> performHealthCheck( |
| | | @Valid @RequestBody DeviceConfigRequest request) { |
| | | try { |
| | | DeviceConfigVO.HealthCheckResult result = deviceConfigService.performHealthCheck(request.getDeviceId()); |
| | | DeviceConfigVO.HealthCheckResult result = deviceConfigService.performHealthCheck(request.getId()); |
| | | return Result.success(result); |
| | | } catch (Exception e) { |
| | | log.error("设备健康检查失败", e); |
| | |
| | | /** |
| | | * 测试设备PLC连接 |
| | | * 支持两种方式: |
| | | * 1. 传入 deviceId,根据已保存的设备配置测试 |
| | | * 1. 传入 id,根据已保存的设备配置测试 |
| | | * 2. 直接传入 plcIp / plcPort / timeout 进行一次性测试 |
| | | */ |
| | | @PostMapping("/devices/test-connection") |
| | |
| | | Integer plcPort = null; |
| | | Integer timeoutMs = null; |
| | | |
| | | // 优先根据 deviceId 读取已保存配置 |
| | | Object deviceIdObj = body.get("deviceId"); |
| | | if (deviceIdObj != null) { |
| | | Long deviceId = deviceIdObj instanceof Number |
| | | ? ((Number) deviceIdObj).longValue() |
| | | : Long.parseLong(deviceIdObj.toString()); |
| | | DeviceConfig device = deviceConfigService.getDeviceById(deviceId); |
| | | // 优先根据 id 读取已保存配置 |
| | | Object idObj = body.get("id"); |
| | | if (idObj != null) { |
| | | Long id = idObj instanceof Number |
| | | ? ((Number) idObj).longValue() |
| | | : Long.parseLong(idObj.toString()); |
| | | DeviceConfig device = deviceConfigService.getDeviceById(id); |
| | | if (device == null) { |
| | | return Result.error("设备不存在: " + deviceId); |
| | | return Result.error("设备不存在: " + id); |
| | | } |
| | | plcIp = device.getPlcIp(); |
| | | plcPort = device.getPlcPort(); |
| | |
| | | public Result<Void> addDeviceToGroup( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | | deviceGroupRelationService.addDeviceToGroup(request.getGroupId(), request.getDeviceId(), |
| | | deviceGroupRelationService.addDeviceToGroup(request.getGroupId(), request.getId(), |
| | | request.getDeviceRole() != null ? request.getDeviceRole() : "MEMBER"); |
| | | return Result.success(null); |
| | | } catch (Exception e) { |
| | |
| | | public Result<Void> removeDeviceFromGroup( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | | deviceGroupRelationService.removeDeviceFromGroup(request.getGroupId(), request.getDeviceId()); |
| | | deviceGroupRelationService.removeDeviceFromGroup(request.getGroupId(), request.getId()); |
| | | return Result.success(null); |
| | | } catch (Exception e) { |
| | | log.error("从设备组移除设备失败", e); |
| | |
| | | public Result<Void> updateDeviceRole( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | | deviceGroupRelationService.updateDeviceRole(request.getGroupId(), request.getDeviceId(), |
| | | deviceGroupRelationService.updateDeviceRole(request.getGroupId(), request.getId(), |
| | | request.getDeviceRole()); |
| | | return Result.success(null); |
| | | } catch (Exception e) { |
| | |
| | | public Result<List<DeviceGroupVO.GroupInfo>> getDeviceGroups( |
| | | @Valid @RequestBody DeviceGroupRequest request) { |
| | | try { |
| | | List<DeviceGroupVO.GroupInfo> groups = deviceGroupRelationService.getDeviceGroups(request.getDeviceId()); |
| | | List<DeviceGroupVO.GroupInfo> groups = deviceGroupRelationService.getDeviceGroups(request.getId()); |
| | | return Result.success(groups); |
| | | } catch (Exception e) { |
| | | log.error("获取设备设备组列表失败", e); |
| | |
| | | public Result<DevicePlcVO.OperationResult> executeOperation( |
| | | @Valid @RequestBody DeviceOperationRequest request) { |
| | | return Result.success(deviceInteractionService.executeOperation( |
| | | request.getDeviceId(), |
| | | request.getId(), |
| | | request.getOperation(), |
| | | request.getParams() |
| | | )); |
| | |
| | | public static class DeviceOperationRequest { |
| | | @NotNull(message = "设备ID不能为空") |
| | | @ApiParam(value = "设备ID", required = true) |
| | | private Long deviceId; |
| | | private Long id; |
| | | |
| | | @NotNull(message = "操作类型不能为空") |
| | | @ApiParam(value = "操作类型(如:feedGlass, triggerRequest, triggerReport等)", required = true) |
| | |
| | | @Valid @RequestBody DeviceStatusUpdateRequest request) { |
| | | try { |
| | | boolean success = deviceStatusService.updateDeviceOnlineStatus( |
| | | request.getDeviceId(), |
| | | request.getId(), |
| | | request.getStatus() |
| | | ); |
| | | if (success) { |
| | |
| | | public static class DeviceStatusUpdateRequest { |
| | | @NotNull(message = "设备ID不能为空") |
| | | @ApiParam(value = "设备配置ID", required = true) |
| | | private Long deviceId; |
| | | private Long id; |
| | | |
| | | @NotEmpty(message = "设备状态不能为空") |
| | | @ApiParam(value = "设备状态(ONLINE/OFFLINE/BUSY/ERROR/MAINTENANCE)", required = true) |
| | |
| | | @Data |
| | | public static class DeviceHeartbeatRequest { |
| | | @NotEmpty(message = "设备ID不能为空") |
| | | @ApiParam(value = "设备ID(device_config.device_id)", required = true) |
| | | @ApiParam(value = "设备ID(DeviceConfig.id的字符串形式,对应device_status.device_id)", required = true) |
| | | private String deviceId; |
| | | |
| | | @ApiParam(value = "设备状态(可选,默认为ONLINE)") |
| | |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | @ApiModelProperty(value = "设备唯一标识", example = "DEVICE_001") |
| | | @TableField("device_id") |
| | | private String deviceId; |
| | | |
| | | @ApiModelProperty(value = "设备名称", example = "大车设备1") |
| | | @TableField("device_name") |
| | | private String deviceName; |
| | |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | @ApiModelProperty(value = "设备ID(device_config.device_id)", example = "DEVICE_001") |
| | | @ApiModelProperty(value = "设备ID(DeviceConfig.id的字符串形式)", example = "1") |
| | | @TableField("device_id") |
| | | private String deviceId; |
| | | |
| | |
| | | "CASE WHEN ds.status = 'ONLINE' THEN TRUE ELSE FALSE END as isOnline " + |
| | | "FROM device_config d " + |
| | | "INNER JOIN device_group_relation dgr ON d.id = dgr.device_id " + |
| | | "LEFT JOIN device_status ds ON d.device_id = ds.device_id " + |
| | | " AND ds.id = (SELECT MAX(id) FROM device_status WHERE device_id = d.device_id) " + |
| | | "LEFT JOIN device_status ds ON CAST(d.id AS CHAR) = ds.device_id " + |
| | | " AND ds.id = (SELECT MAX(id) FROM device_status WHERE device_id = CAST(d.id AS CHAR)) " + |
| | | "WHERE dgr.group_id = #{groupId} AND dgr.is_deleted = 0 AND d.is_deleted = 0 " + |
| | | "ORDER BY dgr.connection_order ASC") |
| | | List<DeviceGroupVO.DeviceInfo> getGroupDevices(@Param("groupId") Long groupId); |
| | |
| | | @Select("SELECT COUNT(DISTINCT d.id) " + |
| | | "FROM device_config d " + |
| | | "INNER JOIN device_group_relation dgr ON d.id = dgr.device_id " + |
| | | "LEFT JOIN device_status ds ON d.device_id = ds.device_id " + |
| | | " AND ds.id = (SELECT MAX(id) FROM device_status WHERE device_id = d.device_id) " + |
| | | "LEFT JOIN device_status ds ON CAST(d.id AS CHAR) = ds.device_id " + |
| | | " AND ds.id = (SELECT MAX(id) FROM device_status WHERE device_id = CAST(d.id AS CHAR)) " + |
| | | "WHERE dgr.group_id = #{groupId} " + |
| | | " AND dgr.is_deleted = 0 " + |
| | | " AND d.is_deleted = 0 " + |
| | |
| | | public class DeviceConfigRequest { |
| | | |
| | | @ApiModelProperty(value = "设备ID", example = "1") |
| | | private Long deviceId; |
| | | private Long id; |
| | | |
| | | @ApiModelProperty(value = "设备配置信息") |
| | | private Object deviceConfig; |
| | |
| | | public DeviceConfigRequest() { |
| | | } |
| | | |
| | | public DeviceConfigRequest(Long deviceId) { |
| | | this.deviceId = deviceId; |
| | | public DeviceConfigRequest(Long id) { |
| | | this.id = id; |
| | | } |
| | | |
| | | public DeviceConfigRequest(Long deviceId, Object deviceConfig) { |
| | | this.deviceId = deviceId; |
| | | public DeviceConfigRequest(Long id, Object deviceConfig) { |
| | | this.id = id; |
| | | this.deviceConfig = deviceConfig; |
| | | } |
| | | |
| | |
| | | |
| | | @NotNull |
| | | @ApiModelProperty(value = "设备ID", required = true) |
| | | private Long deviceId; |
| | | private Long id; |
| | | |
| | | @ApiModelProperty(value = "玻璃ID列表", example = "GLS001") |
| | | private List<String> glassIds; |
| | |
| | | private Long groupId; |
| | | |
| | | @ApiModelProperty(value = "设备ID", example = "1") |
| | | private Long deviceId; |
| | | private Long id; |
| | | |
| | | @ApiModelProperty(value = "设备ID列表") |
| | | private List<Long> deviceIds; |
| | |
| | | this.groupId = groupId; |
| | | } |
| | | |
| | | public DeviceGroupRequest(Long groupId, Long deviceId) { |
| | | public DeviceGroupRequest(Long groupId, Long id) { |
| | | this.groupId = groupId; |
| | | this.deviceId = deviceId; |
| | | this.id = id; |
| | | } |
| | | |
| | | public DeviceGroupRequest(Long groupId, Long deviceId, String deviceRole) { |
| | | public DeviceGroupRequest(Long groupId, Long id, String deviceRole) { |
| | | this.groupId = groupId; |
| | | this.deviceId = deviceId; |
| | | this.id = id; |
| | | this.deviceRole = deviceRole; |
| | | } |
| | | |
| | |
| | | throw new IllegalArgumentException("设备编码已存在"); |
| | | } |
| | | |
| | | // 兼容旧字段:统一将 device_id 填为 deviceCode,避免非空/唯一约束问题 |
| | | deviceConfig.setDeviceId(code); |
| | | |
| | | // 项目ID未传则使用默认项目(单项目场景可用),避免非空约束 |
| | | if (deviceConfig.getProjectId() == null) { |
| | | deviceConfig.setProjectId(1L); |
| | |
| | | if (isDeviceCodeExists(deviceConfig.getDeviceCode(), deviceConfig.getId())) { |
| | | log.warn("设备编号已存在: {}", deviceConfig.getDeviceCode()); |
| | | return false; |
| | | } |
| | | |
| | | // 同步 device_id 与 deviceCode,保持一致 |
| | | if (StringUtils.isNotBlank(deviceConfig.getDeviceCode())) { |
| | | deviceConfig.setDeviceId(deviceConfig.getDeviceCode().trim()); |
| | | } |
| | | |
| | | // 若项目ID缺失,使用默认项目 |
| | |
| | | @Override |
| | | public DevicePlcVO.OperationResult feedGlass(DeviceGlassFeedRequest request) { |
| | | // 优先使用新的处理器架构 |
| | | DeviceConfig deviceConfig = deviceConfigService.getDeviceById(request.getDeviceId()); |
| | | DeviceConfig deviceConfig = deviceConfigService.getDeviceById(request.getId()); |
| | | if (deviceConfig != null) { |
| | | DeviceLogicHandler handler = handlerFactory.getHandler(deviceConfig.getDeviceType()); |
| | | if (handler != null) { |
| | |
| | | } |
| | | |
| | | // 降级到原有逻辑(兼容旧代码) |
| | | DeviceControlProfile profile = controlProfileService.getProfile(request.getDeviceId()); |
| | | DeviceControlProfile profile = controlProfileService.getProfile(request.getId()); |
| | | Map<String, Object> payload = buildGlassPayload(profile, request); |
| | | String opName = "玻璃上料"; |
| | | if (request.getPositionCode() != null) { |
| | | opName = opName + "(" + request.getPositionCode() + ")"; |
| | | } |
| | | return devicePlcOperationService.writeFields(request.getDeviceId(), payload, opName); |
| | | return devicePlcOperationService.writeFields(request.getId(), payload, opName); |
| | | } |
| | | |
| | | /** |
| | |
| | | return false; |
| | | } |
| | | |
| | | String deviceIdStr = device.getDeviceId(); |
| | | String deviceIdStr = String.valueOf(device.getId()); |
| | | if (deviceIdStr == null || deviceIdStr.trim().isEmpty()) { |
| | | log.warn("设备配置中device_id字段为空: id={}", deviceId); |
| | | log.warn("设备配置中id字段为空: id={}", deviceId); |
| | | return false; |
| | | } |
| | | |
| | |
| | | } |
| | | try { |
| | | DeviceConfig device = deviceConfigService.getDeviceById(deviceConfigId); |
| | | if (device == null || device.getDeviceId() == null) { |
| | | if (device == null || device.getId() == null) { |
| | | return null; |
| | | } |
| | | return getLatestByDeviceId(device.getDeviceId()); |
| | | return getLatestByDeviceId(String.valueOf(device.getId())); |
| | | } catch (Exception e) { |
| | | log.error("根据设备配置ID获取设备状态失败: deviceConfigId={}", deviceConfigId, e); |
| | | return null; |
| | |
| | | return result; |
| | | } |
| | | |
| | | // 工程号生成:每次导入都生成新的工程号(先只生成,不保存到数据库,等到MES调用成功后再保存) |
| | | final String engineerId = engineeringSequenceService.generateEngineeringId(new Date()); |
| | | // 工程号:代表整个Excel表,优先使用Excel中的工程号(从第一行或任意一行获取),如果所有行都没有则自动生成 |
| | | String engineerIdFromExcel = null; |
| | | for (Map<String, Object> row : excelRows) { |
| | | String engineeringId = str(row.get("engineeringId")); |
| | | if (engineeringId != null && !engineeringId.trim().isEmpty()) { |
| | | engineerIdFromExcel = engineeringId.trim(); |
| | | break; // 找到第一个非空的工程号就使用 |
| | | } |
| | | } |
| | | final String engineerId = engineerIdFromExcel != null |
| | | ? engineerIdFromExcel |
| | | : engineeringSequenceService.generateEngineeringId(new Date()); |
| | | final String filmsIdDefault = firstValue(excelRows, "filmsId", "白玻"); |
| | | final double thicknessDefault = parseDouble(firstValue(excelRows, "thickness"), 0d); |
| | | |
| | |
| | | // 生成日期字符串(yyMMdd格式),用于流程卡ID生成 |
| | | LocalDate localDate = new Date().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); |
| | | String dateStr = localDate.format(DateTimeFormatter.ofPattern("yyMMdd")); |
| | | |
| | | // 检查是否有流程卡ID:如果所有行的流程卡ID都为空,则所有记录共享同一个流程卡ID |
| | | boolean allFlowCardIdEmpty = excelRows.stream() |
| | | .allMatch(row -> { |
| | | String flowCardId = str(row.get("flowCardId")); |
| | | return flowCardId == null || flowCardId.trim().isEmpty(); |
| | | }); |
| | | // 如果所有流程卡ID都为空,生成一个共享的流程卡ID |
| | | String sharedFlowCardId = allFlowCardIdEmpty ? "NG" + dateStr + "01A001" : null; |
| | | |
| | | // 用于存储每个玻璃ID对应的流程卡ID(同一玻璃ID的多个玻璃共享同一个流程卡ID) |
| | | Map<String, String> glassIdFlowCardIdMap = new HashMap<>(); |
| | |
| | | |
| | | String glassId = str(row.get("glassId")); |
| | | String filmsId = strOrDefault(row.get("filmsId"), filmsIdDefaultFinal); |
| | | String flowCardId = str(row.get("flowCardId")); |
| | | |
| | | // 如果流程卡ID为空,按新规则生成:NG + yyMMdd + 序号(两位,使用玻璃ID) + A001 |
| | | if (flowCardId.isEmpty()) { |
| | | // 检查是否已为该玻璃ID生成过流程卡ID(同一玻璃ID的多个玻璃共享同一个流程卡ID) |
| | | String generatedFlowCardId = glassIdFlowCardIdMap.get(glassId); |
| | | if (generatedFlowCardId == null) { |
| | | // 使用玻璃ID作为序号(解析为整数,如果解析失败则使用1) |
| | | int sequence; |
| | | try { |
| | | sequence = Integer.parseInt(glassId.trim()); |
| | | if (sequence <= 0) { |
| | | sequence = 1; |
| | | } |
| | | } catch (NumberFormatException e) { |
| | | log.warn("玻璃ID无法解析为整数,使用默认值1: glassId={}", glassId); |
| | | sequence = 1; |
| | | } |
| | | generatedFlowCardId = "NG" + dateStr + String.format("%02d", sequence) + "A001"; |
| | | glassIdFlowCardIdMap.put(glassId, generatedFlowCardId); |
| | | log.info("为玻璃ID {} 生成流程卡ID: flowCardId={}", glassId, generatedFlowCardId); |
| | | } |
| | | flowCardId = generatedFlowCardId; |
| | | // 流程卡ID:如果Excel中有,使用Excel的;如果为空,使用共享的流程卡ID |
| | | String flowCardIdFromExcel = str(row.get("flowCardId")); |
| | | String flowCardId; |
| | | if (flowCardIdFromExcel != null && !flowCardIdFromExcel.trim().isEmpty()) { |
| | | // Excel中有流程卡ID,使用Excel的 |
| | | flowCardId = flowCardIdFromExcel.trim(); |
| | | } else { |
| | | // Excel中流程卡ID为空,使用共享的流程卡ID |
| | | flowCardId = sharedFlowCardId != null ? sharedFlowCardId : getOrGenerateFlowCardId(glassId, dateStr, glassIdFlowCardIdMap); |
| | | } |
| | | // 去掉尾部 "/数字"(如果有) |
| | | String baseFlowCardId = flowCardId.replaceFirst("/\\d+$", ""); |
| | | |
| | | // 层号:如果Excel中有,使用Excel的;如果没有,默认1 |
| | | Object layerObj = row.get("layer"); |
| | | int layer = layerObj != null ? (int) parseDouble(layerObj, 1) : 1; |
| | | if (layer <= 0) { |
| | | layer = 1; |
| | | } |
| | | |
| | | // orderNumber 是整型(玻璃类型),从 Excel 读取或使用默认值 1 |
| | | Object orderNumberObj = row.get("orderNumber"); |
| | |
| | | m.put("height", height); |
| | | m.put("thickness", thickness); |
| | | m.put("filmsId", filmsId); |
| | | m.put("layer", 1); |
| | | m.put("layer", layer); |
| | | m.put("totalLayer", 1); |
| | | m.put("edgWidth", width); |
| | | m.put("edgHeight", height); |
| | |
| | | Map<String, Map<String, Object>> flowCardMap = new HashMap<>(); |
| | | for (Map<String, Object> row : excelRows) { |
| | | String glassId = str(row.get("glassId")); |
| | | String flowCardId = str(row.get("flowCardId")); |
| | | if (flowCardId.isEmpty()) { |
| | | // 使用已生成的流程卡ID(与glassInfolList中的逻辑保持一致) |
| | | flowCardId = glassIdFlowCardIdMap.get(glassId); |
| | | if (flowCardId == null) { |
| | | // 如果未生成,则按规则生成(理论上不应该走到这里,因为glassInfolList已经生成过) |
| | | int sequence; |
| | | try { |
| | | sequence = Integer.parseInt(glassId.trim()); |
| | | if (sequence <= 0) { |
| | | sequence = 1; |
| | | } |
| | | } catch (NumberFormatException e) { |
| | | log.warn("玻璃ID无法解析为整数,使用默认值1: glassId={}", glassId); |
| | | sequence = 1; |
| | | } |
| | | flowCardId = "NG" + dateStr + String.format("%02d", sequence) + "A001"; |
| | | glassIdFlowCardIdMap.put(glassId, flowCardId); |
| | | log.warn("流程卡ID未在glassInfolList中生成,此处补充生成: flowCardId={}, glassId={}", flowCardId, glassId); |
| | | } |
| | | // 流程卡ID:如果Excel中有,使用Excel的;如果为空,使用共享的流程卡ID(与glassInfolList逻辑一致) |
| | | String flowCardIdFromExcel = str(row.get("flowCardId")); |
| | | String flowCardId; |
| | | if (flowCardIdFromExcel != null && !flowCardIdFromExcel.trim().isEmpty()) { |
| | | // Excel中有流程卡ID,使用Excel的 |
| | | flowCardId = flowCardIdFromExcel.trim(); |
| | | } else { |
| | | // Excel中流程卡ID为空,使用共享的流程卡ID |
| | | flowCardId = sharedFlowCardId != null ? sharedFlowCardId : getOrGenerateFlowCardId(glassId, dateStr, glassIdFlowCardIdMap); |
| | | } |
| | | // 去掉尾部 "/数字"(如果有) |
| | | flowCardId = flowCardId.replaceFirst("/\\d+$", ""); |
| | |
| | | return def; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取或生成流程卡ID |
| | | * 如果已存在则返回,否则生成新的流程卡ID并缓存 |
| | | * 前端格式:原始glassId + 两位序号(如"101"、"102"、"201"),通过除以100去掉最后两位提取原始glassId |
| | | * |
| | | * @param glassId 玻璃ID(如"101"、"102"、"201") |
| | | * @param dateStr 日期字符串(yyMMdd格式) |
| | | * @param glassIdFlowCardIdMap 玻璃ID到流程卡ID的映射缓存 |
| | | * @return 流程卡ID(格式:NG + yyMMdd + 序号(两位) + A001) |
| | | */ |
| | | private String getOrGenerateFlowCardId(String glassId, String dateStr, Map<String, String> glassIdFlowCardIdMap) { |
| | | String flowCardId = glassIdFlowCardIdMap.get(glassId); |
| | | if (flowCardId == null) { |
| | | // 从glassId中提取原始数字:101 -> 1, 102 -> 1, 201 -> 2 |
| | | int sequence = 1; |
| | | if (glassId != null && !glassId.trim().isEmpty()) { |
| | | try { |
| | | String cleaned = glassId.trim().split("[\\r\\n\\t\\s]+")[0]; |
| | | if (cleaned.matches("\\d+")) { |
| | | int num = Integer.parseInt(cleaned); |
| | | // 如果长度>=3,除以100去掉最后两位(前端追加的序号) |
| | | sequence = (cleaned.length() >= 3 && num >= 100) ? num / 100 : (num > 0 ? num : 1); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("从glassId中提取原始数字失败: glassId={}", glassId, e); |
| | | } |
| | | } |
| | | flowCardId = "NG" + dateStr + String.format("%02d", sequence) + "A001"; |
| | | glassIdFlowCardIdMap.put(glassId, flowCardId); |
| | | log.info("为玻璃ID {} 生成流程卡ID: flowCardId={}", glassId, flowCardId); |
| | | } |
| | | return flowCardId; |
| | | } |
| | | |
| | | /** |
| | | * 保留两位小数(四舍五入) |
| | |
| | | return; |
| | | } |
| | | |
| | | // 如果工程号已存在,先删除该工程号下的旧数据,实现覆盖更新 |
| | | List<GlassInfo> existingGlassInfos = getGlassInfosByEngineeringId(engineeringId.trim()); |
| | | if (!existingGlassInfos.isEmpty()) { |
| | | log.info("检测到工程号 {} 已存在 {} 条记录,将删除旧数据并更新", engineeringId, existingGlassInfos.size()); |
| | | deleteGlassInfosByEngineeringId(engineeringId.trim()); |
| | | } |
| | | |
| | | List<GlassInfo> glassInfos = new ArrayList<>(); |
| | | Date now = new Date(); |
| | | |
| | |
| | | |
| | | if (deviceConfig != null) { |
| | | log.info("选择可用车辆: deviceId={}, deviceName={}", |
| | | deviceConfig.getDeviceId(), deviceConfig.getDeviceName()); |
| | | String.valueOf(deviceConfig.getId()), deviceConfig.getDeviceName()); |
| | | } |
| | | |
| | | return deviceConfig; |
| | |
| | | */ |
| | | public List<DeviceConfig> getAvailableVehiclesInGroup(Long groupId) { |
| | | return getVehiclesInGroup(groupId).stream() |
| | | .filter(v -> statusManager.isVehicleAvailable(v.getDeviceId())) |
| | | .filter(v -> statusManager.isVehicleAvailable(String.valueOf(v.getId()))) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | DeviceConfig currentDevice = context.getCurrentDevice(); |
| | | String deviceId = currentDevice.getDeviceId(); |
| | | String deviceId = String.valueOf(currentDevice.getId()); |
| | | |
| | | // 1. 检查车辆状态(如果设备已指定) |
| | | if (deviceId != null) { |
| | |
| | | } |
| | | |
| | | // 4. 标记车辆为执行中 |
| | | String selectedDeviceId = selectedDevice.getDeviceId(); |
| | | String selectedDeviceId = String.valueOf(selectedDevice.getId()); |
| | | statusManager.updateVehicleStatus( |
| | | selectedDeviceId, |
| | | selectedDevice.getDeviceName(), |
| | |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | String deviceId = deviceConfig.getDeviceId(); |
| | | String deviceId = String.valueOf(deviceConfig.getId()); |
| | | log.info("执行大车设备操作: deviceId={}, deviceName={}, operation={}", |
| | | deviceId, deviceConfig.getDeviceName(), operation); |
| | | |
| | |
| | | Map<String, Object> logicParams) { |
| | | |
| | | VehicleTask task = new VehicleTask(); |
| | | task.setTaskId(generateTaskId(deviceConfig.getDeviceId())); |
| | | task.setTaskId(generateTaskId(String.valueOf(deviceConfig.getId()))); |
| | | task.setTaskName("大车设备-" + operation); |
| | | task.setOperation(operation); |
| | | |
| | |
| | | Boolean triggerRequest = (Boolean) params.getOrDefault("triggerRequest", autoFeed); |
| | | |
| | | List<GlassInfo> plannedGlasses = planGlassLoading(glassInfos, vehicleCapacity, glassGap, |
| | | deviceConfig.getDeviceId()); |
| | | String.valueOf(deviceConfig.getId())); |
| | | if (plannedGlasses == null) { |
| | | // 玻璃没有长度时返回null表示错误 |
| | | return DevicePlcVO.OperationResult.builder() |
| | |
| | | // 如果执行成功,更新位置信息到状态 |
| | | if (Boolean.TRUE.equals(result.getSuccess())) { |
| | | VehicleStatus status = statusManager.getOrCreateVehicleStatus( |
| | | deviceConfig.getDeviceId(), deviceConfig.getDeviceName()); |
| | | String.valueOf(deviceConfig.getId()), deviceConfig.getDeviceName()); |
| | | if (positionCode != null || positionValue != null) { |
| | | VehiclePosition position = new VehiclePosition(positionCode, positionValue); |
| | | status.setCurrentPosition(position); |
| | |
| | | |
| | | // 重置时,清除任务并恢复为空闲状态,停止监控 |
| | | if (Boolean.TRUE.equals(result.getSuccess())) { |
| | | statusManager.clearVehicleTask(deviceConfig.getDeviceId()); |
| | | statusManager.updateVehicleStatus(deviceConfig.getDeviceId(), VehicleState.IDLE); |
| | | stopStateMonitoring(deviceConfig.getDeviceId()); |
| | | statusManager.clearVehicleTask(String.valueOf(deviceConfig.getId())); |
| | | statusManager.updateVehicleStatus(String.valueOf(deviceConfig.getId()), VehicleState.IDLE); |
| | | stopStateMonitoring(String.valueOf(deviceConfig.getId())); |
| | | handleStopTaskMonitor(deviceConfig); |
| | | handleStopIdleMonitor(deviceConfig); |
| | | updateDeviceOnlineStatus(deviceConfig, true); |
| | | } else { |
| | | // 即便重置失败,也尝试停止内部监控,避免任务取消后仍然反复写PLC |
| | | stopStateMonitoring(deviceConfig.getDeviceId()); |
| | | stopStateMonitoring(String.valueOf(deviceConfig.getId())); |
| | | handleStopTaskMonitor(deviceConfig); |
| | | handleStopIdleMonitor(deviceConfig); |
| | | } |
| | |
| | | |
| | | // 清空后,恢复为空闲状态,停止监控 |
| | | if (Boolean.TRUE.equals(result.getSuccess())) { |
| | | statusManager.clearVehicleTask(deviceConfig.getDeviceId()); |
| | | statusManager.updateVehicleStatus(deviceConfig.getDeviceId(), VehicleState.IDLE); |
| | | stopStateMonitoring(deviceConfig.getDeviceId()); |
| | | statusManager.clearVehicleTask(String.valueOf(deviceConfig.getId())); |
| | | statusManager.updateVehicleStatus(String.valueOf(deviceConfig.getId()), VehicleState.IDLE); |
| | | stopStateMonitoring(String.valueOf(deviceConfig.getId())); |
| | | handleStopTaskMonitor(deviceConfig); |
| | | handleStopIdleMonitor(deviceConfig); |
| | | updateDeviceOnlineStatus(deviceConfig, true); |
| | | } else { |
| | | // 写入失败也尝试停止监控,避免任务取消后仍旧运行 |
| | | stopStateMonitoring(deviceConfig.getDeviceId()); |
| | | stopStateMonitoring(String.valueOf(deviceConfig.getId())); |
| | | handleStopTaskMonitor(deviceConfig); |
| | | handleStopIdleMonitor(deviceConfig); |
| | | } |
| | |
| | | deviceStatusService.updateDeviceOnlineStatus(deviceConfig.getId(), status); |
| | | } catch (Exception e) { |
| | | log.warn("同步设备在线状态到数据库失败: deviceId={}, online={}, error={}", |
| | | deviceConfig.getDeviceId(), online, e.getMessage()); |
| | | String.valueOf(deviceConfig.getId()), online, e.getMessage()); |
| | | } |
| | | } |
| | | |
| | |
| | | * 定期检查大车的 state1~6,当检测到 state=1 时自动协调卧转立设备 |
| | | */ |
| | | private void startStateMonitoring(DeviceConfig deviceConfig, Map<String, Object> logicParams) { |
| | | String deviceId = deviceConfig.getDeviceId(); |
| | | String deviceId = String.valueOf(deviceConfig.getId()); |
| | | |
| | | // 如果已经在监控,先停止旧的监控任务 |
| | | stopStateMonitoring(deviceId); |
| | |
| | | * 检查大车状态并协调卧转立设备(内部方法,由监控线程调用) |
| | | */ |
| | | private void checkAndCoordinateState(DeviceConfig deviceConfig) { |
| | | String deviceId = deviceConfig.getDeviceId(); |
| | | String deviceId = String.valueOf(deviceConfig.getId()); |
| | | List<String> alreadyCoordinated = coordinatedStates.get(deviceId); |
| | | if (alreadyCoordinated == null) { |
| | | alreadyCoordinated = new CopyOnWriteArrayList<>(); |
| | |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | String deviceId = deviceConfig.getDeviceId(); |
| | | String deviceId = String.valueOf(deviceConfig.getId()); |
| | | |
| | | // 停止旧的监控任务 |
| | | handleStopIdleMonitor(deviceConfig); |
| | |
| | | * 停止空闲监控 |
| | | */ |
| | | private DevicePlcVO.OperationResult handleStopIdleMonitor(DeviceConfig deviceConfig) { |
| | | String deviceId = deviceConfig.getDeviceId(); |
| | | String deviceId = String.valueOf(deviceConfig.getId()); |
| | | ScheduledFuture<?> future = idleMonitoringTasks.remove(deviceId); |
| | | if (future != null && !future.isCancelled()) { |
| | | future.cancel(false); |
| | |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | String deviceId = deviceConfig.getDeviceId(); |
| | | String deviceId = String.valueOf(deviceConfig.getId()); |
| | | PlcClient plcClient = getPlcClient(deviceConfig); |
| | | if (plcClient == null) { |
| | | return DevicePlcVO.OperationResult.builder() |
| | |
| | | */ |
| | | private Integer getCurrentPosition(DeviceConfig deviceConfig, Map<String, Object> logicParams) { |
| | | // 从状态管理器获取 |
| | | VehicleStatus status = statusManager.getVehicleStatus(deviceConfig.getDeviceId()); |
| | | VehicleStatus status = statusManager.getVehicleStatus(String.valueOf(deviceConfig.getId())); |
| | | if (status != null && status.getCurrentPosition() != null) { |
| | | return status.getCurrentPosition().getPositionValue(); |
| | | } |
| | |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | String deviceId = deviceConfig.getDeviceId(); |
| | | String deviceId = String.valueOf(deviceConfig.getId()); |
| | | |
| | | // 停止旧的监控任务 |
| | | handleStopTaskMonitor(deviceConfig); |
| | |
| | | MesTaskInfo taskInfo, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | String deviceId = deviceConfig.getDeviceId(); |
| | | String deviceId = String.valueOf(deviceConfig.getId()); |
| | | PlcClient plcClient = getPlcClient(deviceConfig); |
| | | if (plcClient == null) { |
| | | return; |
| | |
| | | if (taskInfo.brokenGlassIndices != null && taskInfo.brokenGlassIndices.contains(i)) { |
| | | updateStateIfNeeded(deviceConfig, plcClient, stateValues, stateField, 8, taskInfo); |
| | | log.info("玻璃标记为破损: deviceId={}, stateField={}, glassIndex={}", |
| | | deviceConfig.getDeviceId(), stateField, i); |
| | | String.valueOf(deviceConfig.getId()), stateField, i); |
| | | continue; |
| | | } |
| | | |
| | |
| | | if (elapsed >= state3TimeoutTime && (currentState == null || currentState < 2)) { |
| | | updateStateIfNeeded(deviceConfig, plcClient, stateValues, stateField, 3, taskInfo); |
| | | log.warn("任务超时未完成: deviceId={}, stateField={}, elapsed={}ms, expectedTime={}ms", |
| | | deviceConfig.getDeviceId(), stateField, elapsed, state2Time); |
| | | String.valueOf(deviceConfig.getId()), stateField, elapsed, state2Time); |
| | | continue; |
| | | } |
| | | |
| | |
| | | // 注意:如果当前state已经是3(未完成)或8(破损),不再更新 |
| | | if (currentState != null && (currentState == 3 || currentState == 8)) { |
| | | log.debug("任务状态已为异常状态,不再更新: deviceId={}, stateField={}, currentState={}, targetState={}", |
| | | deviceConfig.getDeviceId(), stateField, currentState, targetState); |
| | | String.valueOf(deviceConfig.getId()), stateField, currentState, targetState); |
| | | return false; |
| | | } |
| | | |
| | |
| | | plcClient.writeData(payload); |
| | | |
| | | log.info("任务状态已更新到PLC: deviceId={}, stateField={}, currentState={}, targetState={}", |
| | | deviceConfig.getDeviceId(), stateField, currentState, targetState); |
| | | String.valueOf(deviceConfig.getId()), stateField, currentState, targetState); |
| | | // 返回true表示状态发生了变化 |
| | | return true; |
| | | } catch (Exception e) { |
| | | log.error("写入PLC state字段失败: deviceId={}, stateField={}, targetState={}, error={}", |
| | | deviceConfig.getDeviceId(), stateField, targetState, e.getMessage()); |
| | | String.valueOf(deviceConfig.getId()), stateField, targetState, e.getMessage()); |
| | | return false; |
| | | } |
| | | } |
| | |
| | | .map(g -> g.glassId) |
| | | .collect(java.util.stream.Collectors.joining(",")); |
| | | log.info("已给MES汇报({}任务): deviceId={}, glassCount={}, glassIds=[{}]", |
| | | taskType, deviceConfig.getDeviceId(), taskInfo.glasses.size(), glassIds); |
| | | taskType, String.valueOf(deviceConfig.getId()), taskInfo.glasses.size(), glassIds); |
| | | |
| | | // 多设备任务场景下,不在这里阻塞等待MES确认,由任务引擎定时调用checkMesConfirm |
| | | } catch (Exception e) { |
| | | log.error("给MES汇报异常: deviceId={}", deviceConfig.getDeviceId(), e); |
| | | log.error("给MES汇报异常: deviceId={}", String.valueOf(deviceConfig.getId()), e); |
| | | } |
| | | } |
| | | |
| | |
| | | public DevicePlcVO.OperationResult checkMesConfirm(DeviceConfig deviceConfig, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | String deviceId = deviceConfig.getDeviceId(); |
| | | String deviceId = String.valueOf(deviceConfig.getId()); |
| | | PlcClient plcClient = getPlcClient(deviceConfig); |
| | | if (plcClient == null) { |
| | | return DevicePlcVO.OperationResult.builder() |
| | |
| | | } catch (Exception e) { |
| | | log.warn("MES确认超时时清空任务状态失败: deviceId={}, error={}", deviceId, e.getMessage()); |
| | | } |
| | | statusManager.updateVehicleStatus(deviceConfig.getDeviceId(), VehicleState.ERROR); |
| | | statusManager.clearVehicleTask(deviceConfig.getDeviceId()); |
| | | currentTasks.remove(deviceConfig.getDeviceId()); |
| | | statusManager.updateVehicleStatus(String.valueOf(deviceConfig.getId()), VehicleState.ERROR); |
| | | statusManager.clearVehicleTask(String.valueOf(deviceConfig.getId())); |
| | | currentTasks.remove(String.valueOf(deviceConfig.getId())); |
| | | handleStopTaskMonitor(deviceConfig); |
| | | |
| | | return DevicePlcVO.OperationResult.builder() |
| | |
| | | |
| | | // 任务完成,恢复为空闲状态 |
| | | statusManager.updateVehicleStatus( |
| | | deviceConfig.getDeviceId(), VehicleState.IDLE); |
| | | statusManager.clearVehicleTask(deviceConfig.getDeviceId()); |
| | | String.valueOf(deviceConfig.getId()), VehicleState.IDLE); |
| | | statusManager.clearVehicleTask(String.valueOf(deviceConfig.getId())); |
| | | |
| | | // 移除任务记录(如果有) |
| | | currentTasks.remove(deviceConfig.getDeviceId()); |
| | | currentTasks.remove(String.valueOf(deviceConfig.getId())); |
| | | |
| | | // 停止任务监控 |
| | | handleStopTaskMonitor(deviceConfig); |
| | |
| | | payload.put("plcRequest", 1); |
| | | plcClient.writeData(payload); |
| | | |
| | | log.info("MES任务已确认完成: deviceId={}", deviceConfig.getDeviceId()); |
| | | log.info("MES任务已确认完成: deviceId={}", String.valueOf(deviceConfig.getId())); |
| | | String taskType = (taskInfo != null && taskInfo.isOutbound) ? "出片" : "进片"; |
| | | return DevicePlcVO.OperationResult.builder() |
| | | .success(true) |
| | |
| | | .data(data) |
| | | .build(); |
| | | } catch (Exception e) { |
| | | log.error("检查MES确认状态异常: deviceId={}", deviceConfig.getDeviceId(), e); |
| | | log.error("检查MES确认状态异常: deviceId={}", String.valueOf(deviceConfig.getId()), e); |
| | | return DevicePlcVO.OperationResult.builder() |
| | | .success(false) |
| | | .message("检查MES确认状态异常: " + e.getMessage()) |
| | |
| | | private DevicePlcVO.OperationResult handleMarkBroken(DeviceConfig deviceConfig, |
| | | Map<String, Object> params, |
| | | Map<String, Object> logicParams) { |
| | | String deviceId = deviceConfig.getDeviceId(); |
| | | String deviceId = String.valueOf(deviceConfig.getId()); |
| | | MesTaskInfo taskInfo = currentTasks.get(deviceId); |
| | | if (taskInfo == null) { |
| | | return DevicePlcVO.OperationResult.builder() |
| | |
| | | payload.put("plcReport", 0); |
| | | plcClient.writeData(payload); |
| | | } catch (Exception e) { |
| | | log.error("清空任务状态异常: deviceId={}", deviceConfig.getDeviceId(), e); |
| | | log.error("清空任务状态异常: deviceId={}", String.valueOf(deviceConfig.getId()), e); |
| | | } |
| | | } |
| | | |
| | |
| | | * 停止任务监控 |
| | | */ |
| | | private DevicePlcVO.OperationResult handleStopTaskMonitor(DeviceConfig deviceConfig) { |
| | | String deviceId = deviceConfig.getDeviceId(); |
| | | String deviceId = String.valueOf(deviceConfig.getId()); |
| | | ScheduledFuture<?> future = taskMonitoringTasks.remove(deviceId); |
| | | if (future != null && !future.isCancelled()) { |
| | | future.cancel(false); |
| | |
| | | Map<String, Object> logicParams, |
| | | Map<String, Object> params) { |
| | | |
| | | String deviceId = deviceConfig.getDeviceId(); |
| | | String deviceId = String.valueOf(deviceConfig.getId()); |
| | | PlcClient plcClient = getPlcClient(deviceConfig); |
| | | if (plcClient == null) { |
| | | return buildResult(deviceConfig, "checkAndProcess", false, |
| | |
| | | WorkstationLogicConfig config, |
| | | Map<String, Object> logicParams) { |
| | | |
| | | String deviceId = deviceConfig.getDeviceId(); |
| | | String deviceId = String.valueOf(deviceConfig.getId()); |
| | | |
| | | // 停止旧的监控任务 |
| | | handleStopMonitor(deviceConfig); |
| | |
| | | * 停止监控任务 |
| | | */ |
| | | private DevicePlcVO.OperationResult handleStopMonitor(DeviceConfig deviceConfig) { |
| | | String deviceId = deviceConfig.getDeviceId(); |
| | | String deviceId = String.valueOf(deviceConfig.getId()); |
| | | ScheduledFuture<?> future = monitorTasks.remove(deviceId); |
| | | if (future != null && !future.isCancelled()) { |
| | | future.cancel(false); |
| | |
| | | * 清空缓冲队列 |
| | | */ |
| | | private DevicePlcVO.OperationResult handleClearBuffer(DeviceConfig deviceConfig) { |
| | | String deviceId = deviceConfig.getDeviceId(); |
| | | String deviceId = String.valueOf(deviceConfig.getId()); |
| | | glassBuffer.remove(deviceId); |
| | | lastScanTime.remove(deviceId); |
| | | log.info("已清空缓冲队列: deviceId={}", deviceId); |
| | |
| | | } |
| | | |
| | | private String buildCacheKey(DeviceConfig deviceConfig) { |
| | | // 优先使用数据库主键ID |
| | | if (deviceConfig.getId() != null) { |
| | | return "device:" + deviceConfig.getId(); |
| | | } |
| | | // 备用方案:使用设备编码 |
| | | if (deviceConfig.getDeviceCode() != null) { |
| | | return "device:" + deviceConfig.getDeviceCode(); |
| | | } |
| | | // 最后方案:使用对象哈希 |
| | | return "device:" + Objects.hash(deviceConfig); |
| | | } |
| | | |
| | |
| | | // 使用新的PLC客户端读取数据 |
| | | Map<String, Object> currentData = plcClient.readAllData(); |
| | | if (currentData != null && !currentData.isEmpty()) { |
| | | // 检查联机状态 |
| | | Integer onlineState = parseInteger(currentData.get("onlineState")); |
| | | if (onlineState != null && onlineState == OFF) { |
| | | log.info("当前PLC联机模式为0,停止联机: deviceId={}", deviceId); |
| | | return false; |
| | | // 检查联机状态(仅当配置中存在该字段时) |
| | | if (hasFieldInConfig(device, "onlineState")) { |
| | | Object onlineStateObj = currentData.get("onlineState"); |
| | | if (onlineStateObj != null) { |
| | | Integer onlineState = parseInteger(onlineStateObj); |
| | | if (onlineState == OFF) { |
| | | log.info("当前PLC联机模式为0,停止联机: deviceId={}", deviceId); |
| | | return false; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 检查汇报字,如果为1则重置为0 |
| | |
| | | return false; |
| | | } |
| | | |
| | | // 检查联机状态 |
| | | Object onlineStateObj = currentData.get("onlineState"); |
| | | Integer onlineState = null; |
| | | if (onlineStateObj != null) { |
| | | if (onlineStateObj instanceof Number) { |
| | | onlineState = ((Number) onlineStateObj).intValue(); |
| | | } else { |
| | | try { |
| | | String strValue = String.valueOf(onlineStateObj); |
| | | if (!strValue.isEmpty() && !"null".equalsIgnoreCase(strValue)) { |
| | | onlineState = Integer.parseInt(strValue); |
| | | // 检查联机状态(仅当配置中存在该字段时) |
| | | if (hasFieldInConfig(device, "onlineState")) { |
| | | Object onlineStateObj = currentData.get("onlineState"); |
| | | Integer onlineState = null; |
| | | if (onlineStateObj != null) { |
| | | if (onlineStateObj instanceof Number) { |
| | | onlineState = ((Number) onlineStateObj).intValue(); |
| | | } else { |
| | | try { |
| | | String strValue = String.valueOf(onlineStateObj); |
| | | if (!strValue.isEmpty() && !"null".equalsIgnoreCase(strValue)) { |
| | | onlineState = Integer.parseInt(strValue); |
| | | } |
| | | } catch (NumberFormatException e) { |
| | | log.warn("解析onlineState失败: deviceId={}, value={}", device.getId(), onlineStateObj, e); |
| | | } |
| | | } catch (NumberFormatException e) { |
| | | log.warn("解析onlineState失败: deviceId={}, value={}", device.getId(), onlineStateObj, e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (onlineState != null && onlineState == OFF) { |
| | | log.info("当前PLC联机模式为0,停止联机: deviceId={}", device.getId()); |
| | | return false; |
| | | if (onlineState != null && onlineState == OFF) { |
| | | log.info("当前PLC联机模式为0,停止联机: deviceId={}", device.getId()); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | // 检查汇报字,如果为1则重置为0 |
| | |
| | | // 尝试使用新的PLC客户端工厂 |
| | | PlcClient plcClient = plcClientFactory.getClient(device); |
| | | if (plcClient != null) { |
| | | // 构建重置数据 |
| | | // 构建重置数据(只添加配置中存在的字段) |
| | | Map<String, Object> resetData = new HashMap<>(); |
| | | resetData.put("plcRequest", OFF); |
| | | resetData.put("plcReport", OFF); |
| | | resetData.put("mesSend", OFF); |
| | | resetData.put("mesConfirm", OFF); |
| | | resetData.put("onlineState", ON); |
| | | resetData.put("alarmInfo", OFF); |
| | | if (hasFieldInConfig(device, "plcRequest")) { |
| | | resetData.put("plcRequest", OFF); |
| | | } |
| | | if (hasFieldInConfig(device, "plcReport")) { |
| | | resetData.put("plcReport", OFF); |
| | | } |
| | | if (hasFieldInConfig(device, "mesSend")) { |
| | | resetData.put("mesSend", OFF); |
| | | } |
| | | if (hasFieldInConfig(device, "mesConfirm")) { |
| | | resetData.put("mesConfirm", OFF); |
| | | } |
| | | if (hasFieldInConfig(device, "onlineState")) { |
| | | resetData.put("onlineState", ON); |
| | | } |
| | | if (hasFieldInConfig(device, "alarmInfo")) { |
| | | resetData.put("alarmInfo", OFF); |
| | | } |
| | | |
| | | // 检查是否有字段需要重置 |
| | | if (resetData.isEmpty()) { |
| | | log.warn("设备配置中未找到任何可重置的字段: deviceId={}", deviceId); |
| | | return false; |
| | | } |
| | | |
| | | // 使用新的PLC客户端写入数据 |
| | | boolean success = plcClient.writeData(resetData); |
| | |
| | | return false; |
| | | } |
| | | |
| | | // 构建重置数据 |
| | | // 构建重置数据(只添加配置中存在的字段) |
| | | Map<String, Object> resetData = new HashMap<>(); |
| | | resetData.put("plcRequest", OFF); |
| | | resetData.put("plcReport", OFF); |
| | | resetData.put("mesSend", OFF); |
| | | resetData.put("mesConfirm", OFF); |
| | | resetData.put("onlineState", ON); |
| | | resetData.put("alarmInfo", OFF); |
| | | if (hasFieldInConfig(device, "plcRequest")) { |
| | | resetData.put("plcRequest", OFF); |
| | | } |
| | | if (hasFieldInConfig(device, "plcReport")) { |
| | | resetData.put("plcReport", OFF); |
| | | } |
| | | if (hasFieldInConfig(device, "mesSend")) { |
| | | resetData.put("mesSend", OFF); |
| | | } |
| | | if (hasFieldInConfig(device, "mesConfirm")) { |
| | | resetData.put("mesConfirm", OFF); |
| | | } |
| | | if (hasFieldInConfig(device, "onlineState")) { |
| | | resetData.put("onlineState", ON); |
| | | } |
| | | if (hasFieldInConfig(device, "alarmInfo")) { |
| | | resetData.put("alarmInfo", OFF); |
| | | } |
| | | |
| | | // 检查是否有字段需要重置 |
| | | if (resetData.isEmpty()) { |
| | | log.warn("设备配置中未找到任何可重置的字段: deviceId={}", device.getId()); |
| | | return false; |
| | | } |
| | | |
| | | // 使用PlcDynamicDataService写入数据 |
| | | plcDynamicDataService.writePlcData(device, resetData, s7Serializer); |
| | |
| | | |
| | | throw new IllegalStateException("无法解析设备的PLC项目标识, deviceId=" + device.getId()); |
| | | } |
| | | |
| | | /** |
| | | * 检查设备配置中是否存在指定字段 |
| | | * |
| | | * @param device 设备配置 |
| | | * @param fieldName 字段名 |
| | | * @return 是否存在 |
| | | */ |
| | | private boolean hasFieldInConfig(DeviceConfig device, String fieldName) { |
| | | if (device == null || fieldName == null || fieldName.isEmpty()) { |
| | | return false; |
| | | } |
| | | |
| | | try { |
| | | // 从 configJson 中检查(新结构) |
| | | Map<String, Object> configParams = ConfigJsonHelper.parseToMap(device.getConfigJson(), objectMapper); |
| | | if (configParams.containsKey(fieldName)) { |
| | | return true; |
| | | } |
| | | |
| | | // 从 extraParams.addressMapping 中检查(兼容旧结构) |
| | | Map<String, Object> extraParams = parseExtraParams(device); |
| | | Object addressMapping = extraParams.get("addressMapping"); |
| | | if (addressMapping != null) { |
| | | Map<String, Object> addressMappingMap; |
| | | if (addressMapping instanceof Map) { |
| | | addressMappingMap = (Map<String, Object>) addressMapping; |
| | | } else if (addressMapping instanceof String) { |
| | | addressMappingMap = objectMapper.readValue((String) addressMapping, MAP_TYPE); |
| | | } else { |
| | | return false; |
| | | } |
| | | return addressMappingMap.containsKey(fieldName); |
| | | } |
| | | } catch (Exception e) { |
| | | log.warn("检查字段是否存在时出错: deviceId={}, fieldName={}", device.getId(), fieldName, e); |
| | | } |
| | | |
| | | return false; |
| | | } |
| | | } |
| | |
| | | try { |
| | | String addressMapping = extractAddressMapping(device); |
| | | if (addressMapping == null || addressMapping.isEmpty()) { |
| | | log.error("设备配置中addressMapping为空: deviceId={}", device.getId()); |
| | | return; |
| | | String errorMsg = "设备配置中addressMapping为空: deviceId=" + device.getId(); |
| | | log.error(errorMsg); |
| | | throw new IllegalArgumentException(errorMsg); |
| | | } |
| | | |
| | | // 解析addressMapping JSON配置 |
| | |
| | | String dbArea = extractDbArea(device); |
| | | List<S7Parameter> parameters = buildS7ParametersWithValuesForDevice(device, dbArea, addressMappingObj, dataMap); |
| | | |
| | | if (parameters.isEmpty()) { |
| | | log.warn("没有有效的字段需要写入PLC: deviceId={}", device.getId()); |
| | | return; |
| | | } |
| | | |
| | | // 写入PLC |
| | | s7Serializer.write(parameters); |
| | | } catch (Exception e) { |
| | | log.error("写入PLC数据失败,请检查:1.PLC IP地址是否正确[{}] 2.PLC设备是否在线 3.网络连接是否正常,deviceId: {}, 详细错误: {}", |
| | | device.getPlcIp(), device.getId(), e.getMessage(), e); |
| | | String errorMsg = String.format("写入PLC数据失败,请检查:1.PLC IP地址是否正确[%s] 2.PLC设备是否在线 3.网络连接是否正常,deviceId: %s, 详细错误: %s", |
| | | device.getPlcIp(), device.getId(), e.getMessage()); |
| | | log.error(errorMsg, e); |
| | | throw new RuntimeException(errorMsg, e); |
| | | } |
| | | } |
| | | |
| | |
| | | EDataType dataType = fieldConfig.dataType != null ? fieldConfig.dataType : determineFieldTypeByName(fieldName); |
| | | int count = fieldConfig.count > 0 ? fieldConfig.count : determineFieldCountByName(fieldName); |
| | | |
| | | // 根据字段类型转换值 |
| | | Object convertedValue = convertValueByType(value, dataType); |
| | | |
| | | // 创建S7Parameter,设置值 |
| | | S7Parameter parameter = new S7Parameter(fullAddress, dataType, count); |
| | | parameter.setValue(value); |
| | | parameter.setValue(convertedValue); |
| | | parameters.add(parameter); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * 根据字段类型转换值 |
| | | * 主要处理:Integer -> Boolean (对于BOOL类型) |
| | | * |
| | | * @param value 原始值 |
| | | * @param dataType 目标数据类型 |
| | | * @return 转换后的值 |
| | | */ |
| | | private Object convertValueByType(Object value, EDataType dataType) { |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | |
| | | // 如果已经是目标类型,直接返回 |
| | | if (dataType == EDataType.BOOL) { |
| | | if (value instanceof Boolean) { |
| | | return value; |
| | | } |
| | | // 将 Integer/Number 转换为 Boolean |
| | | if (value instanceof Number) { |
| | | int intValue = ((Number) value).intValue(); |
| | | return intValue != 0; |
| | | } |
| | | // 尝试从字符串转换 |
| | | if (value instanceof String) { |
| | | String str = ((String) value).trim().toLowerCase(); |
| | | return "true".equals(str) || "1".equals(str) || "on".equals(str); |
| | | } |
| | | // 其他类型,尝试转换为数字再转Boolean |
| | | try { |
| | | int intValue = Integer.parseInt(String.valueOf(value)); |
| | | return intValue != 0; |
| | | } catch (NumberFormatException e) { |
| | | log.warn("无法将值转换为Boolean: {}", value); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | // 对于其他类型,如果已经是目标类型或兼容类型,直接返回 |
| | | // 例如:UINT16 可以接受 Integer, Short, Byte 等 |
| | | if (dataType == EDataType.UINT16 || dataType == EDataType.INT16) { |
| | | if (value instanceof Number) { |
| | | return value; |
| | | } |
| | | // 尝试从字符串转换 |
| | | if (value instanceof String) { |
| | | try { |
| | | return Integer.parseInt((String) value); |
| | | } catch (NumberFormatException e) { |
| | | log.warn("无法将值转换为Integer: {}", value); |
| | | return 0; |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 对于其他类型,直接返回原值 |
| | | return value; |
| | | } |
| | | |
| | | /** |
| | | * 根据字段名推断字段长度/数量 |
| | | */ |
| | | private int determineFieldCountByName(String fieldName) { |
| | |
| | | List<TaskStepDetail> largeGlassSteps = taskStepDetailMapper.selectList( |
| | | Wrappers.<TaskStepDetail>lambdaQuery() |
| | | .eq(TaskStepDetail::getTaskId, task.getTaskId()) |
| | | .eq(TaskStepDetail::getDeviceId, largeGlassDevice.getId()) |
| | | .eq(TaskStepDetail::getDeviceId, String.valueOf(largeGlassDevice.getId())) |
| | | .orderByDesc(TaskStepDetail::getStepOrder) |
| | | .last("LIMIT 1") |
| | | ); |
| | |
| | | List<TaskStepDetail> transferSteps = taskStepDetailMapper.selectList( |
| | | Wrappers.<TaskStepDetail>lambdaQuery() |
| | | .eq(TaskStepDetail::getTaskId, taskId) |
| | | .eq(TaskStepDetail::getDeviceId, transferDevice.getId()) |
| | | .eq(TaskStepDetail::getDeviceId, String.valueOf(transferDevice.getId())) |
| | | .orderByDesc(TaskStepDetail::getStepOrder) |
| | | .last("LIMIT 1") |
| | | ); |
| | |
| | | url: `/api/plcSend/device/config/devices/update`, |
| | | method: 'post', |
| | | data: { |
| | | deviceId: id, |
| | | id: id, |
| | | deviceConfig: data |
| | | } |
| | | }) |
| | |
| | | return request({ |
| | | url: `/api/plcSend/device/config/devices/delete`, |
| | | method: 'post', |
| | | data: { deviceId: id } |
| | | data: { id: id } |
| | | }) |
| | | }, |
| | | |
| | |
| | | return request({ |
| | | url: `/api/plcSend/device/config/devices/detail`, |
| | | method: 'post', |
| | | data: { deviceId: id } |
| | | data: { id: id } |
| | | }) |
| | | }, |
| | | |
| | |
| | | return request({ |
| | | url: '/api/plcSend/device/config/devices/enable', |
| | | method: 'post', |
| | | data: { deviceId: id } |
| | | data: { id: id } |
| | | }) |
| | | }, |
| | | |
| | |
| | | return request({ |
| | | url: '/api/plcSend/device/config/devices/disable', |
| | | method: 'post', |
| | | data: { deviceId: id } |
| | | data: { id: id } |
| | | }) |
| | | }, |
| | | |
| | |
| | | method: 'post', |
| | | data: { |
| | | deviceCode, |
| | | excludeId |
| | | id: excludeId |
| | | } |
| | | }) |
| | | }, |
| | |
| | | |
| | | /** |
| | | * 测试设备PLC连接 |
| | | * data 可以是 { deviceId } 或 { plcIp, plcPort, timeout } |
| | | * data 可以是 { id } 或 { plcIp, plcPort, timeout } |
| | | */ |
| | | testConnection(data) { |
| | | return request({ |
| | |
| | | return request({ |
| | | url: `/api/plcSend/device/config/devices/health-check`, |
| | | method: 'post', |
| | | data: { deviceId: id } |
| | | data: { id: id } |
| | | }) |
| | | } |
| | | } |
| | |
| | | export const deviceInteractionApi = { |
| | | /** |
| | | * 执行设备逻辑操作 |
| | | * @param {Object} data - { deviceId, operation, params } |
| | | * @param {Object} data - { id, operation, params } |
| | | */ |
| | | executeOperation(data) { |
| | | return request({ |
| | |
| | | export const deviceStatusApi = { |
| | | /** |
| | | * 更新设备在线状态 |
| | | * @param {Object} data - { deviceId, status } |
| | | * @param {Object} data - { id, status } |
| | | */ |
| | | updateDeviceOnlineStatus(data) { |
| | | return request({ |
| | |
| | | showConfirmButton: false |
| | | }) |
| | | |
| | | const response = await deviceConfigApi.testConnection({ deviceId: row.id }) |
| | | const response = await deviceConfigApi.testConnection({ id: row.id }) |
| | | |
| | | if (response.success) { |
| | | ElMessage.success(response.data || `设备 ${row.deviceName} 连接测试成功`) |
| | |
| | | </el-table-column> |
| | | <el-table-column prop="plcIp" label="PLC IP" width="130" /> |
| | | <el-table-column prop="plcType" label="PLC类型" width="100" /> |
| | | <el-table-column prop="moduleName" label="模块名称" min-width="120" /> |
| | | <el-table-column prop="moduleName" label="模块名称" min-width="60" /> |
| | | <el-table-column prop="isPrimary" label="主控设备" width="100" align="center"> |
| | | <template #default="scope"> |
| | | <el-tag v-if="scope.row.isPrimary" type="success" size="small">主控</el-tag> |
| | |
| | | {{ formatDateTime(scope.row.lastHeartbeat) }} |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" width="200" fixed="right"> |
| | | <el-table-column label="操作" width="300" fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button type="primary" size="small" @click="editDevice(scope.row)"> |
| | | 编辑 |
| | | </el-button> |
| | | <el-button type="warning" size="small" :loading="plcOperationLoading" @click="handleSinglePlcRequest(scope.row)"> |
| | | <el-button type="warning" size="small" :loading="plcOperationLoading" @click.stop="handleSinglePlcRequest(scope.row, $event)"> |
| | | PLC请求 |
| | | </el-button> |
| | | <el-button type="success" size="small" @click="healthCheck(scope.row)"> |
| | |
| | | } |
| | | } |
| | | |
| | | const handleSinglePlcRequest = (row) => executePlcOperation([row.id || row.deviceId], 'request') |
| | | const handleSinglePlcReport = (row) => executePlcOperation([row.id || row.deviceId], 'report') |
| | | const handleSinglePlcReset = (row) => executePlcOperation([row.id || row.deviceId], 'reset') |
| | | const handleSinglePlcRequest = (row, event) => { |
| | | if (event) { |
| | | event.stopPropagation() |
| | | } |
| | | executePlcOperation([row.id || row.deviceId], 'request') |
| | | } |
| | | const handleSinglePlcReport = (row, event) => { |
| | | if (event) { |
| | | event.stopPropagation() |
| | | } |
| | | executePlcOperation([row.id || row.deviceId], 'report') |
| | | } |
| | | const handleSinglePlcReset = (row, event) => { |
| | | if (event) { |
| | | event.stopPropagation() |
| | | } |
| | | executePlcOperation([row.id || row.deviceId], 'reset') |
| | | } |
| | | |
| | | const batchPlcRequest = () => executePlcOperation(getSelectedDeviceIds(), 'request') |
| | | const batchPlcReport = () => executePlcOperation(getSelectedDeviceIds(), 'report') |
| | |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" width="200" fixed="right"> |
| | | <el-table-column label="操作" width="280" fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button |
| | | v-if="scope.row.isOnline" |
| | |
| | | :loading="scope.row.statusUpdating" |
| | | > |
| | | 设为在线 |
| | | </el-button> |
| | | <el-button |
| | | type="danger" |
| | | size="small" |
| | | @click="removeSingleDevice(scope.row)" |
| | | > |
| | | 移除设备 |
| | | </el-button> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | } |
| | | } |
| | | |
| | | // 移除单个设备 |
| | | const removeSingleDevice = async (device) => { |
| | | try { |
| | | await ElMessageBox.confirm( |
| | | `确定要从设备组中移除设备"${device.deviceName || device.deviceCode}"吗?`, |
| | | '移除设备确认', |
| | | { |
| | | confirmButtonText: '确定移除', |
| | | cancelButtonText: '取消', |
| | | type: 'warning' |
| | | } |
| | | ) |
| | | |
| | | const deviceId = device.id || device.deviceId |
| | | await deviceGroupApi.batchRemoveDevicesFromGroup({ |
| | | groupId: currentGroup.value.id || currentGroup.value.groupId, |
| | | deviceIds: [deviceId] |
| | | }) |
| | | ElMessage.success('设备移除成功') |
| | | const groupId = currentGroup.value.id || currentGroup.value.groupId |
| | | await loadGroupDevices(groupId) |
| | | await loadAvailableDevices() |
| | | emit('refresh-statistics') |
| | | } catch (error) { |
| | | if (error !== 'cancel') { |
| | | console.error('移除设备失败:', error) |
| | | ElMessage.error('移除设备失败: ' + (error.response?.data?.message || error.message)) |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 更新设备在线状态 |
| | | const updateDeviceOnlineStatus = async (device, status) => { |
| | | try { |
| | |
| | | } |
| | | |
| | | await deviceStatusApi.updateDeviceOnlineStatus({ |
| | | deviceId: deviceId, |
| | | id: deviceId, |
| | | status: status |
| | | }) |
| | | |
| | |
| | | try { |
| | | togglingDeviceId.value = deviceId |
| | | await deviceInteractionApi.executeOperation({ |
| | | deviceId, |
| | | id: deviceId, |
| | | operation: 'setOnlineState', |
| | | params: { |
| | | onlineState: value |
| | |
| | | try { |
| | | clearingDeviceId.value = deviceId |
| | | await deviceInteractionApi.executeOperation({ |
| | | deviceId, |
| | | id: deviceId, |
| | | operation: 'clearPlc' |
| | | }) |
| | | ElMessage.success(`已清空 ${device.deviceName || device.deviceCode} 的PLC数据`) |
| | |
| | | try { |
| | | clearLoading.value = true |
| | | const response = await deviceInteractionApi.executeOperation({ |
| | | deviceId: loadDeviceId.value, |
| | | id: loadDeviceId.value, |
| | | operation: 'clearPlc', |
| | | params: {} |
| | | }) |
| | |
| | | headerStr === '客户名称') { |
| | | headerMap.customerName = index |
| | | } |
| | | // 层号 |
| | | else if (headerStr.includes('层号') || headerStr.includes('layer') || |
| | | headerStr === '层') { |
| | | headerMap.layer = index |
| | | } |
| | | // 工程号 |
| | | else if (headerStr.includes('工程号') || headerStr.includes('engineeringid') || |
| | | headerStr.includes('engineering') || headerStr === '工程id') { |
| | | headerMap.engineeringId = index |
| | | } |
| | | }) |
| | | |
| | | // 如果没有找到表头,尝试使用第一行作为表头(索引方式) |
| | |
| | | const flowCardId = row[headerMap.flowCardId] ? String(row[headerMap.flowCardId]).trim() : '' |
| | | const productName = row[headerMap.productName] ? String(row[headerMap.productName]).trim() : '' |
| | | const customerName = row[headerMap.customerName] ? String(row[headerMap.customerName]).trim() : '' |
| | | const layer = row[headerMap.layer] ? String(row[headerMap.layer]).trim() : '' |
| | | const engineeringId = row[headerMap.engineeringId] ? String(row[headerMap.engineeringId]).trim() : '' |
| | | |
| | | // 跳过空行 |
| | | if (!glassId && !width && !length && !thickness && !quantity) { |
| | |
| | | return isNaN(num) ? '0' : String(num) |
| | | } |
| | | |
| | | // 处理数量:如果数量大于1,需要生成多条记录 |
| | | // 处理数量:根据数量生成多条记录,每条记录都要补齐序号 |
| | | const qty = parseInt(quantity) || 1 |
| | | for (let j = 0; j < qty; j++) { |
| | | // 如果数量大于1,为每条记录生成唯一的玻璃ID(追加序号) |
| | | const finalGlassId = qty > 1 ? `${glassId}${padTwoZero(j + 1)}` : glassId |
| | | // 为每条记录生成唯一的玻璃ID(追加序号,即使数量为1也要补齐) |
| | | // 例如:glassId="1", quantity=2 -> "101", "102" |
| | | // glassId="2", quantity=1 -> "201" |
| | | const finalGlassId = `${glassId}${padTwoZero(j + 1)}` |
| | | |
| | | result.push({ |
| | | glassId: finalGlassId, |
| | |
| | | thickness: parseNumber(thickness), |
| | | quantity: '1', // 每条记录数量为1 |
| | | filmsId: filmsId, |
| | | flowCardId: flowCardId || finalGlassId, |
| | | flowCardId: flowCardId || '', // 如果Excel中没有流程卡ID,传空字符串让后端生成 |
| | | productName: productName, |
| | | customerName: customerName |
| | | customerName: customerName, |
| | | layer: layer || '', // 层号,如果Excel中没有则为空 |
| | | engineeringId: engineeringId || '' // 工程号,如果Excel中没有则为空 |
| | | }) |
| | | } |
| | | } |