|
@@ -2,6 +2,7 @@ package com.fdkankan.indoor.core.service.impl;
|
|
|
|
|
|
import cn.hutool.core.util.RandomUtil;
|
|
import cn.hutool.core.util.RandomUtil;
|
|
import cn.hutool.core.util.StrUtil;
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
|
+import com.fdkankan.indoor.base.constant.CmdConstant;
|
|
import com.fdkankan.indoor.base.constant.ConfigConstant;
|
|
import com.fdkankan.indoor.base.constant.ConfigConstant;
|
|
import com.fdkankan.indoor.base.constant.MsgCode;
|
|
import com.fdkankan.indoor.base.constant.MsgCode;
|
|
import com.fdkankan.indoor.base.constant.TypeConstant;
|
|
import com.fdkankan.indoor.base.constant.TypeConstant;
|
|
@@ -10,12 +11,11 @@ import com.fdkankan.indoor.base.convert.GisCoordinateUtil;
|
|
import com.fdkankan.indoor.base.convert.ModifyCloud;
|
|
import com.fdkankan.indoor.base.convert.ModifyCloud;
|
|
import com.fdkankan.indoor.base.convert.ModifyDataSets;
|
|
import com.fdkankan.indoor.base.convert.ModifyDataSets;
|
|
import com.fdkankan.indoor.base.exception.BaseRuntimeException;
|
|
import com.fdkankan.indoor.base.exception.BaseRuntimeException;
|
|
|
|
+import com.fdkankan.indoor.base.util.CmdUtils;
|
|
import com.fdkankan.indoor.base.util.Result;
|
|
import com.fdkankan.indoor.base.util.Result;
|
|
import com.fdkankan.indoor.base.util.SnowFlakeUUidUtils;
|
|
import com.fdkankan.indoor.base.util.SnowFlakeUUidUtils;
|
|
import com.fdkankan.indoor.core.entity.*;
|
|
import com.fdkankan.indoor.core.entity.*;
|
|
-import com.fdkankan.indoor.core.entity.dto.FilterHotDto;
|
|
|
|
-import com.fdkankan.indoor.core.entity.dto.SiteDto;
|
|
|
|
-import com.fdkankan.indoor.core.entity.dto.SitePolygon;
|
|
|
|
|
|
+import com.fdkankan.indoor.core.entity.dto.*;
|
|
import com.fdkankan.indoor.core.entity.merge.MergeDateSetDto;
|
|
import com.fdkankan.indoor.core.entity.merge.MergeDateSetDto;
|
|
import com.fdkankan.indoor.core.entity.po.DataSetPo;
|
|
import com.fdkankan.indoor.core.entity.po.DataSetPo;
|
|
import com.fdkankan.indoor.core.service.*;
|
|
import com.fdkankan.indoor.core.service.*;
|
|
@@ -68,6 +68,9 @@ public class MergeSceneServiceImpl extends IBaseServiceImpl implements MergeScen
|
|
@Autowired
|
|
@Autowired
|
|
MergeInfoService mergeInfoService;
|
|
MergeInfoService mergeInfoService;
|
|
|
|
|
|
|
|
+ @Autowired
|
|
|
|
+ TiledMapService tiledMapService;
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* 目前只支持没有合并过的场景
|
|
* 目前只支持没有合并过的场景
|
|
* @param sceneCode
|
|
* @param sceneCode
|
|
@@ -97,19 +100,27 @@ public class MergeSceneServiceImpl extends IBaseServiceImpl implements MergeScen
|
|
log.info("合并场景路径:{}", mergePath);
|
|
log.info("合并场景路径:{}", mergePath);
|
|
|
|
|
|
// siteMode要优先处理;
|
|
// siteMode要优先处理;
|
|
- Map<Object, Object> resMap = mergeSiteModel(sceneCode, mergeCode, mergePath, controlPoint);
|
|
|
|
|
|
+// Map<Object, Object> resMap = mergeSiteModel(sceneCode, mergeCode, mergePath, controlPoint);
|
|
|
|
+ Map<Object, Object> resMap = mergeSiteModelFloor(sceneCode, mergeCode, mergePath, controlPoint);
|
|
|
|
|
|
Integer siteModelId = (Integer)resMap.get("maxId");
|
|
Integer siteModelId = (Integer)resMap.get("maxId");
|
|
|
|
|
|
// dateSet.siteModelId是数组, 如果siteModel的模板id改了,这里要重新设置
|
|
// dateSet.siteModelId是数组, 如果siteModel的模板id改了,这里要重新设置
|
|
- List<Integer> siteModeIds = Arrays.asList(10,11, siteModelId);
|
|
|
|
|
|
+// List<Integer> siteModeIds = Arrays.asList(10,11, siteModelId);
|
|
|
|
+ List<Integer> siteModeIds = Arrays.asList(10, siteModelId);
|
|
Integer dataSetId = mergeDateSet(sceneCode, mergeCode, siteModeIds, mergePath, controlPoint);
|
|
Integer dataSetId = mergeDateSet(sceneCode, mergeCode, siteModeIds, mergePath, controlPoint);
|
|
|
|
|
|
|
|
|
|
mergeFilter(sceneCode, mergeCode, controlPoint, mergePath, siteModelId, dataSetId);
|
|
mergeFilter(sceneCode, mergeCode, controlPoint, mergePath, siteModelId, dataSetId);
|
|
|
|
|
|
|
|
+ // 合并平面图
|
|
|
|
+ mergeTileMap(sceneCode, mergeCode, siteModelId, mergePath);
|
|
|
|
+
|
|
// 上传数据到oss:目录pano、pano_depth、webcloud上传oss 外层目录以场景码命名
|
|
// 上传数据到oss:目录pano、pano_depth、webcloud上传oss 外层目录以场景码命名
|
|
- initService.ossUploadDirByLaserData(sceneCode, mergePath, mergeCode);
|
|
|
|
|
|
+ // todo 本地测试,注释上传oss
|
|
|
|
+ if (!"dev".equals(configConstant.active)) {
|
|
|
|
+ initService.ossUploadDirByLaserData(sceneCode, mergePath, mergeCode);
|
|
|
|
+ }
|
|
log.info("文件上传oss完成, 合并数据完成");
|
|
log.info("文件上传oss完成, 合并数据完成");
|
|
|
|
|
|
// 保存合并信息
|
|
// 保存合并信息
|
|
@@ -329,7 +340,38 @@ public class MergeSceneServiceImpl extends IBaseServiceImpl implements MergeScen
|
|
return strings.get(i);
|
|
return strings.get(i);
|
|
}
|
|
}
|
|
|
|
|
|
- private Map<Object, Object> mergeSiteModel(String sceneCode, String mergeCode, String mergePath, ControlPointEntity controlPoint){
|
|
|
|
|
|
+// private Map<Object, Object> mergeSiteModel(String sceneCode, String mergeCode, String mergePath, ControlPointEntity controlPoint){
|
|
|
|
+//
|
|
|
|
+// // 获取原场景数据
|
|
|
|
+// SiteModelEntity entity = siteModelService.findById(sceneCode);
|
|
|
|
+// if (entity == null) {
|
|
|
|
+// throw new BaseRuntimeException("原SiteModel数据不存在: " + sceneCode);
|
|
|
|
+// }
|
|
|
|
+//
|
|
|
|
+// List<SiteDto> originalData = entity.getData();
|
|
|
|
+//
|
|
|
|
+//
|
|
|
|
+// // 初始化房间数据
|
|
|
|
+// SiteDto roomDto = initMergeSiteModel(mergeCode, mergePath,controlPoint);
|
|
|
|
+// // 添加数据
|
|
|
|
+// Map<Object, Object> resMap = setRoom(originalData, roomDto);
|
|
|
|
+// originalData = (List<SiteDto>) resMap.get("data");
|
|
|
|
+//
|
|
|
|
+//
|
|
|
|
+// // 添加数据
|
|
|
|
+//
|
|
|
|
+// entity.setData(originalData);
|
|
|
|
+// entity.setUpdateTime(LocalDateTime.now());
|
|
|
|
+//
|
|
|
|
+// siteModelService.save(entity);
|
|
|
|
+// log.info("siteModel合并完成");
|
|
|
|
+//
|
|
|
|
+// return resMap;
|
|
|
|
+//
|
|
|
|
+// }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ private Map<Object, Object> mergeSiteModelFloor(String sceneCode, String mergeCode, String mergePath, ControlPointEntity controlPoint){
|
|
|
|
|
|
// 获取原场景数据
|
|
// 获取原场景数据
|
|
SiteModelEntity entity = siteModelService.findById(sceneCode);
|
|
SiteModelEntity entity = siteModelService.findById(sceneCode);
|
|
@@ -341,11 +383,13 @@ public class MergeSceneServiceImpl extends IBaseServiceImpl implements MergeScen
|
|
|
|
|
|
|
|
|
|
// 初始化房间数据
|
|
// 初始化房间数据
|
|
- SiteDto roomDto = initMergeSiteModel(mergeCode, mergePath,controlPoint);
|
|
|
|
|
|
+// SiteDto roomDto = initMergeSiteModel(mergeCode, mergePath,controlPoint);
|
|
|
|
+ // 初始化Floor数据
|
|
|
|
+ SiteDto floorDto = initMergeSiteModel(mergeCode, mergePath,controlPoint);
|
|
|
|
|
|
// 添加数据
|
|
// 添加数据
|
|
- Map<Object, Object> resMap = setRoom(originalData, roomDto);
|
|
|
|
- originalData = (List<SiteDto>) resMap.get("data");
|
|
|
|
|
|
+ Map<Object, Object> resMap = setFloor(originalData, floorDto);
|
|
|
|
+ originalData = (List<SiteDto>) resMap.get("data");
|
|
|
|
|
|
|
|
|
|
// 添加数据
|
|
// 添加数据
|
|
@@ -418,6 +462,66 @@ public class MergeSceneServiceImpl extends IBaseServiceImpl implements MergeScen
|
|
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
|
+ *
|
|
|
|
+ * @param dto 原数据
|
|
|
|
+ * @param floorDto 需要加入的楼层
|
|
|
|
+ */
|
|
|
|
+ private Map<Object, Object> setFloor(List<SiteDto> dto, SiteDto floorDto) {
|
|
|
|
+ // 用来获取最大id
|
|
|
|
+ List<Integer> ids = new ArrayList<>();
|
|
|
|
+
|
|
|
|
+ dto.forEach(p -> {
|
|
|
|
+ ids.add(p.getId());
|
|
|
|
+ List<SiteDto> floorChild = p.getChildren();
|
|
|
|
+ floorChild.forEach(f -> {
|
|
|
|
+ ids.add(f.getId());
|
|
|
|
+
|
|
|
|
+ List<SiteDto> roomChild = f.getChildren();
|
|
|
|
+ roomChild.forEach(r -> {
|
|
|
|
+ ids.add(r.getId());
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 获取最大id
|
|
|
|
+ Integer maxId = ids.stream().reduce(Integer::max).get();
|
|
|
|
+ log.info("最大id: {}" , maxId);
|
|
|
|
+
|
|
|
|
+ for (SiteDto buildingDto : dto) {
|
|
|
|
+ List<SiteDto> floorChild = buildingDto.getChildren();
|
|
|
|
+ maxId ++;
|
|
|
|
+ floorDto.setId(maxId);
|
|
|
|
+ floorChild.add(floorDto);
|
|
|
|
+// for (SiteDto roomDtos : floorChild) {
|
|
|
|
+// maxId ++;
|
|
|
|
+// roomDto.setId(maxId);
|
|
|
|
+// List<SiteDto> children = roomDtos.getChildren();
|
|
|
|
+// // 加入房间
|
|
|
|
+// children.add(roomDto);
|
|
|
|
+// }
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ Map<Object, Object> result = new HashMap<>();
|
|
|
|
+ log.info("返回值最大id: {}" , maxId);
|
|
|
|
+ result.put("maxId", maxId);
|
|
|
|
+ result.put("data", dto);
|
|
|
|
+
|
|
|
|
+// 删除最后一个
|
|
|
|
+// ids.remove(ids.size()-1);
|
|
|
|
+
|
|
|
|
+ // 提供给dataSet使用
|
|
|
|
+ ids.add(maxId);
|
|
|
|
+// result.put("ids", ids);
|
|
|
|
+ return result;
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ /**
|
|
* 初始化DataSet
|
|
* 初始化DataSet
|
|
* location: 原点坐标(特殊点表)
|
|
* location: 原点坐标(特殊点表)
|
|
* bounding_box_max:(特殊点表)
|
|
* bounding_box_max:(特殊点表)
|
|
@@ -499,32 +603,14 @@ public class MergeSceneServiceImpl extends IBaseServiceImpl implements MergeScen
|
|
// 修改cloud.js文件
|
|
// 修改cloud.js文件
|
|
JSONObject info = ModifyCloud.fixCloud(path);
|
|
JSONObject info = ModifyCloud.fixCloud(path);
|
|
|
|
|
|
- // 2021.08.26 合并不需要, 初始化时已经更新写入服务器了
|
|
|
|
-// cn.hutool.core.io.FileUtil.writeUtf8String(info.toString(), path);
|
|
|
|
-// log.info("新的cloud.js写入服务器完成:{}", path);
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* 将boundingBox坐标转换成gis坐标, site_model需要
|
|
* 将boundingBox坐标转换成gis坐标, site_model需要
|
|
* boundingBox:虚拟点坐标,存特殊点, dataSet数据要使用
|
|
* boundingBox:虚拟点坐标,存特殊点, dataSet数据要使用
|
|
*/
|
|
*/
|
|
JSONObject boundingBox = ModifyCloud.getBoundingBox(info);
|
|
JSONObject boundingBox = ModifyCloud.getBoundingBox(info);
|
|
-// Double maxX = boundingBox.getDouble("maxX");
|
|
|
|
-// Double maxY = boundingBox.getDouble("maxY");
|
|
|
|
Double maxZ = boundingBox.getDouble("maxZ");
|
|
Double maxZ = boundingBox.getDouble("maxZ");
|
|
-
|
|
|
|
-// Double minX = boundingBox.getDouble("minX");
|
|
|
|
-// Double minY = boundingBox.getDouble("minY");
|
|
|
|
Double minZ = boundingBox.getDouble("minZ");
|
|
Double minZ = boundingBox.getDouble("minZ");
|
|
|
|
|
|
-// Double centreX = boundingBox.getDouble("centreX");
|
|
|
|
-// Double centreY = boundingBox.getDouble("centreY");
|
|
|
|
-// Double centreZ = boundingBox.getDouble("centreZ");
|
|
|
|
-
|
|
|
|
-
|
|
|
|
- // 虚拟点坐标(四维看看坐标)
|
|
|
|
-// Double[] ageMax = {maxX,maxY,maxZ};
|
|
|
|
-// Double[] ageMin = {minX,minY,minZ};
|
|
|
|
-// Double[] ageCentre = {centreX,centreY,centreZ};
|
|
|
|
|
|
|
|
JSONObject resJson = ModifyCloud.convertFromBoundingBox(boundingBox, dto);
|
|
JSONObject resJson = ModifyCloud.convertFromBoundingBox(boundingBox, dto);
|
|
|
|
|
|
@@ -532,15 +618,9 @@ public class MergeSceneServiceImpl extends IBaseServiceImpl implements MergeScen
|
|
Double[] gpsMin = getKey(resJson, "min");
|
|
Double[] gpsMin = getKey(resJson, "min");
|
|
Double[] gpsCentre = getKeyZ(resJson, "centre");
|
|
Double[] gpsCentre = getKeyZ(resJson, "centre");
|
|
|
|
|
|
- // 将数据保存到db
|
|
|
|
-// saveSpecialPoint(sceneCode, TypeConstant.POI_BOUNDINGBOX_MAX, ageMax, gpsMax);
|
|
|
|
-// saveSpecialPoint(sceneCode, TypeConstant.POI_BOUNDINGBOX_MIN, ageMin, gpsMin);
|
|
|
|
-// saveSpecialPoint(sceneCode, TypeConstant.POI_CENTRE, ageCentre, gpsCentre);
|
|
|
|
-// log.info("max、min、中心点保存成功");
|
|
|
|
-
|
|
|
|
-
|
|
|
|
// 创建sitModel
|
|
// 创建sitModel
|
|
- SiteDto siteModel = createSiteModel(sceneCode, gpsMax, gpsMin, gpsCentre, maxZ, minZ);
|
|
|
|
|
|
+// SiteDto siteModel = createSiteModel(sceneCode, gpsMax, gpsMin, gpsCentre, maxZ, minZ);
|
|
|
|
+ SiteDto siteModel = createSiteModelFloor(sceneCode, gpsMax, gpsMin, gpsCentre, maxZ, minZ);
|
|
return siteModel;
|
|
return siteModel;
|
|
} catch (IOException e) {
|
|
} catch (IOException e) {
|
|
e.printStackTrace();
|
|
e.printStackTrace();
|
|
@@ -616,8 +696,118 @@ public class MergeSceneServiceImpl extends IBaseServiceImpl implements MergeScen
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
+ private SiteDto createSiteModelFloor(String sceneCode, Double[] max, Double[] min, Double[] centre, Double maxZ, Double minZ){
|
|
|
|
+ // z_max、z_min 替换site_model的Floor、root类型
|
|
|
|
+ // max、min四个顶点替换BUILDING、FLOOR 的coordinates,
|
|
|
|
+ // 顺时针,(x_max,y_max),(x_max,y_min),(x_min,y_min),(x_min,y_max)
|
|
|
|
+ // 一般参数1:x, 参数2:Y
|
|
|
|
+ Double[] peak_1 = {max[0],max[1]};
|
|
|
|
+ Double[] peak_2 = {max[0],min[1]};
|
|
|
|
+ Double[] peak_3 = {min[0],min[1]};
|
|
|
|
+ Double[] peak_4 = {min[0],max[1]};
|
|
|
|
+ List<List<Double[]>> doubles = Arrays.asList(Arrays.asList(peak_1, peak_2, peak_3, peak_4));
|
|
|
|
|
|
|
|
+ // centre 替换BUILDING、FLOOR、ROOM 的center
|
|
|
|
+ log.info("中心点:{}, {}, {}", centre[0], centre[1], centre[2]);
|
|
|
|
+
|
|
|
|
+ // 获取模板对象
|
|
|
|
+// SiteDto siteDto = initSiteModelRoom();
|
|
|
|
+ SiteDto siteDto = initSiteModelFloor(sceneCode);
|
|
|
|
+ siteDto.setCenter(centre);
|
|
|
|
+ siteDto.setZ_max(maxZ);
|
|
|
|
+ siteDto.setZ_min(minZ);
|
|
|
|
+
|
|
|
|
+// SitePolygon rPolygon = new SitePolygon();
|
|
|
|
+// rPolygon.setCoordinates(doubles);
|
|
|
|
+// rPolygon.setType("Polygon");
|
|
|
|
+// siteDto.setPolygon(rPolygon);
|
|
|
|
|
|
|
|
+ log.info("siteModel数据初始化创建完成");
|
|
|
|
+ return siteDto;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private SiteDto initSiteModelFloor(String sceneCode){
|
|
|
|
+ SiteDto dto = new SiteDto();
|
|
|
|
+ dto.setType("FLOOR");
|
|
|
|
+ dto.setName("合并FLOOR_" + sceneCode);
|
|
|
|
+// dto.setArea(2503.30551910935);
|
|
|
|
+ dto.setVolume(295513.21880627);
|
|
|
|
+ dto.setGeometry_hash(-200081285L);
|
|
|
|
+ return dto;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ private void mergeTileMap(String sceneCode, String mergeCode, Integer modelSiteId, String path){
|
|
|
|
+ TiledMapEntity mapEntity = tiledMapService.findById(sceneCode);
|
|
|
|
+ if (mapEntity == null){
|
|
|
|
+ log.warn("tiledMap数据不存在");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ List<TiledMapDto> data = mapEntity.getData();
|
|
|
|
+ if (data.size() < 1) {
|
|
|
|
+ log.warn("data没有数据, 不需要合并");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ log.info("tiledMap合并前数量:{}", data.size());
|
|
|
|
+ int maxId = 1;
|
|
|
|
+ if (data.size() > 1) {
|
|
|
|
+ maxId = data.stream().mapToInt(TiledMapDto::getId).max().getAsInt();
|
|
|
|
+ }
|
|
|
|
+ List<TiledMapDto> mergeTiledMapData = getMergeTiledMapData(sceneCode, mergeCode, modelSiteId, maxId, path);
|
|
|
|
+ data.addAll(mergeTiledMapData);
|
|
|
|
+ log.info("tiledMap合并后数量:{}", data.size());
|
|
|
|
+ mapEntity.setData(data);
|
|
|
|
+ tiledMapService.save(mapEntity);
|
|
|
|
+ log.info("tiledMap合并完成");
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ }
|
|
|
|
+ /**
|
|
|
|
+ * 2021-09-06
|
|
|
|
+ * @param sceneCode 场景码
|
|
|
|
+ * @param mergeCode 被合并的场景码
|
|
|
|
+ * @param modelSiteId 合并的floorId
|
|
|
|
+ * @param maxTiledMapId tiledMap最大id
|
|
|
|
+ * @return
|
|
|
|
+ *
|
|
|
|
+ * 复制数据, 直接修改floorId值
|
|
|
|
+ */
|
|
|
|
+ private List<TiledMapDto> getMergeTiledMapData(String sceneCode, String mergeCode, Integer modelSiteId, Integer maxTiledMapId, String path){
|
|
|
|
+ List<TiledMapDto> data = tiledMapService.getDataBySceneCode(mergeCode);
|
|
|
|
+
|
|
|
|
+// String path = redisPath(sceneCode) + "/laserData";
|
|
|
|
+// if ("dev".equals(configConstant.active)) {
|
|
|
|
+// path = "F:\\test\\ngin\\age_laser_data\\" + sceneCode + "\\results\\laserData";
|
|
|
|
+// }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ for (TiledMapDto mapDto : data) {
|
|
|
|
+ maxTiledMapId ++;
|
|
|
|
+ mapDto.setId(maxTiledMapId);
|
|
|
|
+ mapDto.setFloor_id(modelSiteId);
|
|
|
|
+ mapDto.setFile_path("data/bundle_" + mergeCode +"/building_1/map_tiles/" + mapDto.getFloor_id());
|
|
|
|
+
|
|
|
|
+ // 将cover上传oss
|
|
|
|
+ String ossTarget = "data/" + sceneCode + "/" + mapDto.getFile_path();
|
|
|
|
+ String uploadDir = path + "/cover";
|
|
|
|
+ log.info("uploadDir: {}", uploadDir);
|
|
|
|
+ // todo 本地测试,需要注释;
|
|
|
|
+ if (!"dev".equals(configConstant.active)) {
|
|
|
|
+ ossUploadDir(ossTarget, uploadDir);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ return data;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void ossUploadDir(String ossTarget, String uploadDir){
|
|
|
|
+ String cmd = CmdConstant.OSSUTIL_UPLOAD_DIR;
|
|
|
|
+ cmd = cmd.replaceAll("@uploadDir", uploadDir);
|
|
|
|
+ cmd = cmd.replaceAll("@target", ossTarget);
|
|
|
|
+ CmdUtils.ossUploadDir(cmd);
|
|
|
|
+ log.info("TiledMap上传oss完成");
|
|
|
|
+ }
|
|
|
|
|
|
@Test
|
|
@Test
|
|
public void testsub(){
|
|
public void testsub(){
|