| | |
| | | import com.github.xingshuangs.iot.protocol.s7.serializer.S7Parameter; |
| | | import com.mes.device.entity.DeviceConfig; |
| | | import com.mes.device.util.ConfigJsonHelper; |
| | | import com.mes.entity.PlcAddress; |
| | | import com.mes.s7.enhanced.EnhancedS7Serializer; |
| | | import com.mes.service.PlcDynamicDataService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import com.github.xingshuangs.iot.protocol.s7.serializer.S7Variable; |
| | | |
| | | import java.lang.reflect.Field; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.HashMap; |
| | |
| | | |
| | | /** |
| | | * PLC动态数据读写服务实现 |
| | | * 通过PlcAddress中的addressMapping配置动态读写任意字段组合 |
| | | * 通过DeviceConfig中的configJson配置动态读写任意字段组合 |
| | | * |
| | | * @author huang |
| | | * @date 2025/11/05 |
| | |
| | | |
| | | private final ObjectMapper objectMapper = new ObjectMapper(); |
| | | private static final TypeReference<Map<String, Object>> MAP_TYPE = new TypeReference<Map<String, Object>>() {}; |
| | | |
| | | /** |
| | | * 根据PlcAddress配置和字段名称读取PLC数据 |
| | | * |
| | | * @param config PLC地址映射配置 |
| | | * @param fieldNames 要读取的字段名称列表 |
| | | * @param s7Serializer S7序列化器 |
| | | * @return 字段名->值 的Map |
| | | */ |
| | | @Override |
| | | public Map<String, Object> readPlcData(PlcAddress config, List<String> fieldNames, EnhancedS7Serializer s7Serializer) { |
| | | if (config == null || config.getAddressMapping() == null) { |
| | | throw new IllegalArgumentException("PlcAddress配置或addressMapping不能为空"); |
| | | } |
| | | |
| | | try { |
| | | // 解析addressMapping JSON配置 |
| | | JSONObject addressMapping = JSONObject.parseObject(config.getAddressMapping()); |
| | | |
| | | // 构建S7Parameter列表 |
| | | List<S7Parameter> parameters = buildS7Parameters(config, addressMapping, fieldNames); |
| | | |
| | | // 从PLC读取数据 |
| | | List<S7Parameter> results = s7Serializer.read(parameters); |
| | | |
| | | // 将结果转换为Map |
| | | Map<String, Object> resultMap = new HashMap<>(); |
| | | for (int i = 0; i < fieldNames.size() && i < results.size(); i++) { |
| | | String fieldName = fieldNames.get(i); |
| | | Object value = results.get(i).getValue(); |
| | | resultMap.put(fieldName, value); |
| | | } |
| | | |
| | | return resultMap; |
| | | } catch (Exception e) { |
| | | log.error("读取PLC数据失败,请检查:1.PLC IP地址是否正确[{}] 2.PLC设备是否在线 3.网络连接是否正常,详细错误: {}", |
| | | config.getPlcIp(), e.getMessage(), e); |
| | | return new HashMap<>(); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 根据PlcAddress配置和数据Map写入PLC |
| | | * |
| | | * @param config PLC地址映射配置 |
| | | * @param dataMap 字段名->值 的Map |
| | | * @param s7Serializer S7序列化器 |
| | | */ |
| | | @Override |
| | | public void writePlcData(PlcAddress config, Map<String, Object> dataMap, EnhancedS7Serializer s7Serializer) { |
| | | if (config == null || config.getAddressMapping() == null) { |
| | | throw new IllegalArgumentException("PlcAddress配置或addressMapping不能为空"); |
| | | } |
| | | |
| | | try { |
| | | // 解析addressMapping JSON配置 |
| | | JSONObject addressMapping = JSONObject.parseObject(config.getAddressMapping()); |
| | | |
| | | // 构建S7Parameter列表,并填充值 |
| | | List<S7Parameter> parameters = buildS7ParametersWithValues(config, addressMapping, dataMap); |
| | | |
| | | // 写入PLC |
| | | s7Serializer.write(parameters); |
| | | } catch (Exception e) { |
| | | log.error("写入PLC数据失败,请检查:1.PLC IP地址是否正确[{}] 2.PLC设备是否在线 3.网络连接是否正常,详细错误: {}", |
| | | config.getPlcIp(), e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 读取PLC所有字段 |
| | | * |
| | | * @param config PLC地址映射配置 |
| | | * @param s7Serializer S7序列化器 |
| | | * @return 所有字段的值 |
| | | */ |
| | | @Override |
| | | public Map<String, Object> readAllPlcData(PlcAddress config, EnhancedS7Serializer s7Serializer) { |
| | | if (config == null || config.getAddressMapping() == null) { |
| | | throw new IllegalArgumentException("PlcAddress配置或addressMapping不能为空"); |
| | | } |
| | | |
| | | // 获取所有字段名 |
| | | JSONObject addressMapping = JSONObject.parseObject(config.getAddressMapping()); |
| | | List<String> allFields = new ArrayList<>(addressMapping.keySet()); |
| | | |
| | | // 读取所有字段 |
| | | return readPlcData(config, allFields, s7Serializer); |
| | | } |
| | | |
| | | /** |
| | | * 读取单个字段 |
| | | * |
| | | * @param config PLC地址映射配置 |
| | | * @param fieldName 字段名 |
| | | * @param s7Serializer S7序列化器 |
| | | * @return 字段值 |
| | | */ |
| | | @Override |
| | | public Object readPlcField(PlcAddress config, String fieldName, EnhancedS7Serializer s7Serializer) { |
| | | List<String> fields = new ArrayList<>(); |
| | | fields.add(fieldName); |
| | | |
| | | Map<String, Object> result = readPlcData(config, fields, s7Serializer); |
| | | return result.get(fieldName); |
| | | } |
| | | |
| | | /** |
| | | * 写入单个字段 |
| | | * |
| | | * @param config PLC地址映射配置 |
| | | * @param fieldName 字段名 |
| | | * @param value 字段值 |
| | | * @param s7Serializer S7序列化器 |
| | | */ |
| | | @Override |
| | | public void writePlcField(PlcAddress config, String fieldName, Object value, EnhancedS7Serializer s7Serializer) { |
| | | Map<String, Object> dataMap = new HashMap<>(); |
| | | dataMap.put(fieldName, value); |
| | | |
| | | writePlcData(config, dataMap, s7Serializer); |
| | | } |
| | | |
| | | /** |
| | | * 构建S7Parameter列表(不包含值) |
| | | * |
| | | * @param config PLC地址配置 |
| | | * @param addressMapping 地址映射 |
| | | * @param fieldNames 字段名列表 |
| | | * @return S7Parameter列表 |
| | | */ |
| | | private List<S7Parameter> buildS7Parameters(PlcAddress config, JSONObject addressMapping, List<String> fieldNames) { |
| | | List<S7Parameter> parameters = new ArrayList<>(); |
| | | |
| | | for (String fieldName : fieldNames) { |
| | | if (!addressMapping.containsKey(fieldName)) { |
| | | log.warn("字段 {} 在addressMapping中不存在,跳过", fieldName); |
| | | continue; |
| | | } |
| | | |
| | | // 获取字段的偏移地址 |
| | | int offset = addressMapping.getInteger(fieldName); |
| | | |
| | | // 构建完整地址:dbArea + offset(如:DB12.2) |
| | | String fullAddress = config.getDbArea() + "." + offset; |
| | | |
| | | // 创建S7Parameter,默认使用UINT16类型(16位无符号整数) |
| | | S7Parameter parameter = new S7Parameter(fullAddress, EDataType.UINT16, 1); |
| | | parameters.add(parameter); |
| | | } |
| | | |
| | | return parameters; |
| | | } |
| | | |
| | | /** |
| | | * 构建S7Parameter列表(包含值) |
| | | * |
| | | * @param config PLC地址配置 |
| | | * @param addressMapping 地址映射 |
| | | * @param dataMap 字段名->值 的Map |
| | | * @return S7Parameter列表 |
| | | */ |
| | | private List<S7Parameter> buildS7ParametersWithValues(PlcAddress config, JSONObject addressMapping, Map<String, Object> dataMap) { |
| | | List<S7Parameter> parameters = new ArrayList<>(); |
| | | |
| | | for (Map.Entry<String, Object> entry : dataMap.entrySet()) { |
| | | String fieldName = entry.getKey(); |
| | | Object value = entry.getValue(); |
| | | |
| | | if (!addressMapping.containsKey(fieldName)) { |
| | | log.warn("字段 {} 在addressMapping中不存在,跳过", fieldName); |
| | | continue; |
| | | } |
| | | |
| | | // 获取字段的偏移地址 |
| | | int offset = addressMapping.getInteger(fieldName); |
| | | |
| | | // 构建完整地址 |
| | | String fullAddress = config.getDbArea() + "." + offset; |
| | | |
| | | // 创建S7Parameter,设置值 |
| | | S7Parameter parameter = new S7Parameter(fullAddress, EDataType.UINT16, 1); |
| | | parameter.setValue(value); |
| | | parameters.add(parameter); |
| | | } |
| | | |
| | | return parameters; |
| | | } |
| | | |
| | | /** |
| | | * 从DeviceConfig中提取地址映射配置 |
| | |
| | | throw new IllegalArgumentException("设备配置不能为空"); |
| | | } |
| | | |
| | | String addressMapping = extractAddressMapping(device); |
| | | if (addressMapping == null || addressMapping.isEmpty()) { |
| | | throw new IllegalArgumentException("设备配置中addressMapping不能为空"); |
| | | if (s7Serializer == null) { |
| | | log.error("S7Serializer为空,无法写入PLC数据: deviceId={}", device.getId()); |
| | | return; |
| | | } |
| | | |
| | | try { |
| | | String addressMapping = extractAddressMapping(device); |
| | | if (addressMapping == null || addressMapping.isEmpty()) { |
| | | log.error("设备配置中addressMapping为空: deviceId={}", device.getId()); |
| | | return; |
| | | } |
| | | |
| | | // 解析addressMapping JSON配置 |
| | | JSONObject addressMappingObj = JSONObject.parseObject(addressMapping); |
| | | |
| | |
| | | throw new IllegalArgumentException("设备配置不能为空"); |
| | | } |
| | | |
| | | if (s7Serializer == null) { |
| | | log.error("S7Serializer为空,无法读取PLC数据: deviceId={}", device.getId()); |
| | | return new HashMap<>(); |
| | | } |
| | | |
| | | try { |
| | | String addressMapping = extractAddressMapping(device); |
| | | if (addressMapping == null || addressMapping.isEmpty()) { |
| | | throw new IllegalArgumentException("设备配置中addressMapping不能为空"); |
| | | log.error("设备配置中addressMapping为空: deviceId={}", device.getId()); |
| | | return new HashMap<>(); |
| | | } |
| | | |
| | | // 获取所有字段名 |
| | |
| | | |
| | | // 读取所有字段 |
| | | return readPlcData(device, allFields, s7Serializer); |
| | | } catch (Exception e) { |
| | | log.error("读取所有PLC数据失败: deviceId={}", device.getId(), e); |
| | | return new HashMap<>(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | |
| | | writePlcData(device, dataMap, s7Serializer); |
| | | } |
| | | |
| | | @Override |
| | | public <T> void writePlcDataByEntity(DeviceConfig device, T entity, EnhancedS7Serializer s7Serializer) { |
| | | if (device == null || entity == null) { |
| | | throw new IllegalArgumentException("设备配置和实体对象不能为空"); |
| | | } |
| | | |
| | | try { |
| | | // 1. 从configJson中获取地址映射(字段名 -> 偏移量) |
| | | Map<String, Object> addressMapping = ConfigJsonHelper.parseToMap(device.getConfigJson(), objectMapper); |
| | | if (addressMapping.isEmpty()) { |
| | | throw new IllegalArgumentException("设备配置中未找到地址映射配置, deviceId=" + device.getId()); |
| | | } |
| | | |
| | | // 2. 获取dbArea和beginIndex |
| | | String dbArea = extractDbArea(device); |
| | | int beginIndex = extractBeginIndex(device); |
| | | |
| | | // 3. 获取字段配置(类型和count) |
| | | Map<String, FieldConfig> fieldConfigMap = extractFieldConfigMap(device); |
| | | |
| | | // 4. 解析实体类,获取所有带@S7Variable注解的字段 |
| | | Class<?> entityClass = entity.getClass(); |
| | | List<S7Parameter> parameters = new ArrayList<>(); |
| | | |
| | | for (Field field : entityClass.getDeclaredFields()) { |
| | | S7Variable annotation = field.getAnnotation(S7Variable.class); |
| | | if (annotation == null) { |
| | | continue; |
| | | } |
| | | |
| | | // 获取字段名(从注解的address获取,对应configJson中的paramKey) |
| | | String fieldName = annotation.address(); |
| | | if (fieldName == null || fieldName.isEmpty()) { |
| | | continue; |
| | | } |
| | | |
| | | // 从addressMapping中获取偏移量 |
| | | Object offsetObj = addressMapping.get(fieldName); |
| | | if (offsetObj == null) { |
| | | log.warn("字段 {} 在configJson地址映射中不存在,跳过", fieldName); |
| | | continue; |
| | | } |
| | | |
| | | int offset; |
| | | if (offsetObj instanceof Number) { |
| | | offset = ((Number) offsetObj).intValue(); |
| | | } else { |
| | | offset = Integer.parseInt(String.valueOf(offsetObj)); |
| | | } |
| | | |
| | | // 构建完整地址:dbArea + (beginIndex + offset) |
| | | String fullAddress = dbArea + "." + (beginIndex + offset); |
| | | |
| | | // 确定数据类型和count |
| | | // 优先级:1. 注解中的type和count 2. configJson中的配置 3. 根据字段名推断 |
| | | EDataType dataType = annotation.type(); |
| | | int count = annotation.count(); |
| | | |
| | | // 如果注解中没有指定count,尝试从configJson或字段名推断 |
| | | if (count <= 0) { |
| | | FieldConfig fieldConfig = fieldConfigMap.get(fieldName); |
| | | if (fieldConfig != null && fieldConfig.count > 0) { |
| | | count = fieldConfig.count; |
| | | } else { |
| | | count = determineFieldCountByName(fieldName); |
| | | } |
| | | } |
| | | |
| | | // 如果注解中的类型是UINT16但字段是String类型,尝试从configJson获取 |
| | | if (dataType == EDataType.UINT16 && field.getType() == String.class) { |
| | | FieldConfig fieldConfig = fieldConfigMap.get(fieldName); |
| | | if (fieldConfig != null && fieldConfig.dataType != null) { |
| | | dataType = fieldConfig.dataType; |
| | | } else { |
| | | dataType = determineFieldTypeByName(fieldName); |
| | | } |
| | | } |
| | | |
| | | // 获取字段值 |
| | | field.setAccessible(true); |
| | | Object value = field.get(entity); |
| | | if (value == null) { |
| | | continue; // 跳过null值 |
| | | } |
| | | |
| | | // 创建S7Parameter并设置值 |
| | | S7Parameter parameter = new S7Parameter(fullAddress, dataType, count); |
| | | parameter.setValue(value); |
| | | parameters.add(parameter); |
| | | } |
| | | |
| | | if (parameters.isEmpty()) { |
| | | log.warn("实体类 {} 中没有找到有效的字段,无法写入PLC", entityClass.getSimpleName()); |
| | | return; |
| | | } |
| | | |
| | | // 5. 写入PLC |
| | | s7Serializer.write(parameters); |
| | | |
| | | log.info("根据实体类写入PLC数据成功: deviceId={}, entityClass={}, fields={}", |
| | | device.getId(), entityClass.getSimpleName(), parameters.size()); |
| | | |
| | | } catch (Exception e) { |
| | | log.error("根据实体类写入PLC数据失败: deviceId={}, entityClass={}", |
| | | device.getId(), entity != null ? entity.getClass().getSimpleName() : "null", e); |
| | | throw new RuntimeException("写入PLC数据失败: " + e.getMessage(), e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 构建S7Parameter列表(不包含值)- 基于DeviceConfig |
| | | */ |
| | | private List<S7Parameter> buildS7ParametersForDevice(DeviceConfig device, String dbArea, JSONObject addressMapping, List<String> fieldNames) { |
| | | List<S7Parameter> parameters = new ArrayList<>(); |
| | | |
| | | // 获取字段配置(从configJson中解析类型和count) |
| | | Map<String, FieldConfig> fieldConfigMap = extractFieldConfigMap(device); |
| | | |
| | | for (String fieldName : fieldNames) { |
| | | if (!addressMapping.containsKey(fieldName)) { |
| | |
| | | continue; |
| | | } |
| | | |
| | | // 获取字段的偏移地址 |
| | | int offset = addressMapping.getInteger(fieldName); |
| | | // 获取字段的偏移地址(addressMapping中只存储数字偏移量) |
| | | Object offsetObj = addressMapping.get(fieldName); |
| | | int offset; |
| | | if (offsetObj instanceof Number) { |
| | | offset = ((Number) offsetObj).intValue(); |
| | | } else { |
| | | offset = Integer.parseInt(String.valueOf(offsetObj)); |
| | | } |
| | | |
| | | // 构建完整地址:dbArea + offset(如:DB12.2) |
| | | String fullAddress = dbArea + "." + offset; |
| | | |
| | | // 创建S7Parameter,默认使用UINT16类型(16位无符号整数) |
| | | S7Parameter parameter = new S7Parameter(fullAddress, EDataType.UINT16, 1); |
| | | // 获取字段类型和长度(从configJson或根据字段名推断) |
| | | FieldConfig fieldConfig = fieldConfigMap.getOrDefault(fieldName, new FieldConfig()); |
| | | EDataType dataType = fieldConfig.dataType != null ? fieldConfig.dataType : determineFieldTypeByName(fieldName); |
| | | int count = fieldConfig.count > 0 ? fieldConfig.count : determineFieldCountByName(fieldName); |
| | | |
| | | // 创建S7Parameter |
| | | S7Parameter parameter = new S7Parameter(fullAddress, dataType, count); |
| | | parameters.add(parameter); |
| | | } |
| | | |
| | |
| | | private List<S7Parameter> buildS7ParametersWithValuesForDevice(DeviceConfig device, String dbArea, JSONObject addressMapping, Map<String, Object> dataMap) { |
| | | List<S7Parameter> parameters = new ArrayList<>(); |
| | | |
| | | // 获取字段配置(从configJson中解析类型和count) |
| | | Map<String, FieldConfig> fieldConfigMap = extractFieldConfigMap(device); |
| | | |
| | | for (Map.Entry<String, Object> entry : dataMap.entrySet()) { |
| | | String fieldName = entry.getKey(); |
| | | Object value = entry.getValue(); |
| | |
| | | continue; |
| | | } |
| | | |
| | | // 获取字段的偏移地址 |
| | | int offset = addressMapping.getInteger(fieldName); |
| | | // 获取字段的偏移地址(addressMapping中只存储数字偏移量) |
| | | Object offsetObj = addressMapping.get(fieldName); |
| | | int offset; |
| | | if (offsetObj instanceof Number) { |
| | | offset = ((Number) offsetObj).intValue(); |
| | | } else { |
| | | offset = Integer.parseInt(String.valueOf(offsetObj)); |
| | | } |
| | | |
| | | // 构建完整地址 |
| | | String fullAddress = dbArea + "." + offset; |
| | | |
| | | // 获取字段类型和长度(从configJson或根据字段名推断) |
| | | FieldConfig fieldConfig = fieldConfigMap.getOrDefault(fieldName, new FieldConfig()); |
| | | EDataType dataType = fieldConfig.dataType != null ? fieldConfig.dataType : determineFieldTypeByName(fieldName); |
| | | int count = fieldConfig.count > 0 ? fieldConfig.count : determineFieldCountByName(fieldName); |
| | | |
| | | // 创建S7Parameter,设置值 |
| | | S7Parameter parameter = new S7Parameter(fullAddress, EDataType.UINT16, 1); |
| | | S7Parameter parameter = new S7Parameter(fullAddress, dataType, count); |
| | | parameter.setValue(value); |
| | | parameters.add(parameter); |
| | | } |
| | | |
| | | return parameters; |
| | | } |
| | | |
| | | /** |
| | | * 字段配置信息 |
| | | */ |
| | | private static class FieldConfig { |
| | | EDataType dataType; |
| | | int count; |
| | | |
| | | FieldConfig() { |
| | | this.dataType = null; |
| | | this.count = 0; |
| | | } |
| | | |
| | | FieldConfig(EDataType dataType, int count) { |
| | | this.dataType = dataType; |
| | | this.count = count; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 从设备配置中提取字段配置映射(类型和count) |
| | | * configJson格式: [{paramKey: "plcGlassId1", paramValue: "4", description: "玻璃id1", dataType: "STRING", count: 20}] |
| | | */ |
| | | private Map<String, FieldConfig> extractFieldConfigMap(DeviceConfig device) { |
| | | Map<String, FieldConfig> configMap = new HashMap<>(); |
| | | |
| | | try { |
| | | String configJson = device.getConfigJson(); |
| | | if (configJson == null || configJson.trim().isEmpty()) { |
| | | return configMap; |
| | | } |
| | | |
| | | String trimmed = configJson.trim(); |
| | | // 如果configJson是数组格式,解析数组 |
| | | if (trimmed.startsWith("[")) { |
| | | List<Map<String, Object>> paramList = objectMapper.readValue(trimmed, |
| | | new TypeReference<List<Map<String, Object>>>() {}); |
| | | |
| | | for (Map<String, Object> param : paramList) { |
| | | Object paramKeyObj = param.get("paramKey"); |
| | | if (paramKeyObj == null) { |
| | | continue; |
| | | } |
| | | String paramKey = String.valueOf(paramKeyObj); |
| | | |
| | | EDataType dataType = null; |
| | | int count = 0; |
| | | |
| | | // 解析dataType |
| | | Object dataTypeObj = param.get("dataType"); |
| | | if (dataTypeObj != null) { |
| | | String dataTypeStr = String.valueOf(dataTypeObj).toUpperCase(); |
| | | try { |
| | | dataType = EDataType.valueOf(dataTypeStr); |
| | | } catch (IllegalArgumentException e) { |
| | | log.debug("无法解析数据类型: {}, 字段: {}", dataTypeStr, paramKey); |
| | | } |
| | | } |
| | | |
| | | // 解析count |
| | | Object countObj = param.get("count"); |
| | | if (countObj != null) { |
| | | if (countObj instanceof Number) { |
| | | count = ((Number) countObj).intValue(); |
| | | } else { |
| | | try { |
| | | count = Integer.parseInt(String.valueOf(countObj)); |
| | | } catch (NumberFormatException e) { |
| | | log.debug("无法解析count值: {}, 字段: {}", countObj, paramKey); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (dataType != null || count > 0) { |
| | | configMap.put(paramKey, new FieldConfig(dataType, count)); |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | log.debug("解析字段配置映射失败: {}", e.getMessage()); |
| | | } |
| | | |
| | | return configMap; |
| | | } |
| | | |
| | | /** |
| | | * 根据字段名推断数据类型 |
| | | */ |
| | | private EDataType determineFieldTypeByName(String fieldName) { |
| | | if (fieldName == null) { |
| | | return EDataType.UINT16; |
| | | } |
| | | |
| | | String lowerName = fieldName.toLowerCase(); |
| | | // 玻璃ID字段通常是字符串 |
| | | if (lowerName.contains("glassid") || lowerName.contains("glass_id") || |
| | | lowerName.startsWith("plcglassid")) { |
| | | return EDataType.STRING; |
| | | } |
| | | |
| | | // 默认返回UINT16 |
| | | return EDataType.UINT16; |
| | | } |
| | | |
| | | /** |
| | | * 根据字段名推断字段长度/数量 |
| | | */ |
| | | private int determineFieldCountByName(String fieldName) { |
| | | if (fieldName == null) { |
| | | return 1; |
| | | } |
| | | |
| | | String lowerName = fieldName.toLowerCase(); |
| | | // 玻璃ID通常是20个字符 |
| | | if (lowerName.contains("glassid") || lowerName.contains("glass_id") || |
| | | lowerName.startsWith("plcglassid")) { |
| | | return 20; // 默认20个字符 |
| | | } |
| | | |
| | | // 默认返回1 |
| | | return 1; |
| | | } |
| | | } |