|
@@ -1,6 +1,7 @@
|
|
package com.fdkankan.ucenter.service.impl;
|
|
package com.fdkankan.ucenter.service.impl;
|
|
|
|
|
|
import cn.hutool.core.bean.BeanUtil;
|
|
import cn.hutool.core.bean.BeanUtil;
|
|
|
|
+import cn.hutool.core.thread.ThreadUtil;
|
|
import cn.hutool.core.util.StrUtil;
|
|
import cn.hutool.core.util.StrUtil;
|
|
import com.alibaba.fastjson.JSON;
|
|
import com.alibaba.fastjson.JSON;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
@@ -31,6 +32,7 @@ import java.io.File;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.util.*;
|
|
import java.util.*;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.concurrent.CompletableFuture;
|
|
|
|
+import java.util.concurrent.ExecutorService;
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
import com.fdkankan.ucenter.util.RoamingPointUtil;
|
|
import com.fdkankan.ucenter.util.RoamingPointUtil;
|
|
@@ -174,154 +176,163 @@ public class ScenePlusServiceImpl extends ServiceImpl<IScenePlusMapper, ScenePlu
|
|
scenePlus.setSceneStatus(0);
|
|
scenePlus.setSceneStatus(0);
|
|
this.save(scenePlus);
|
|
this.save(scenePlus);
|
|
|
|
|
|
- CompletableFuture.runAsync(() -> {
|
|
|
|
- try {
|
|
|
|
- // 拷贝场景编辑资源
|
|
|
|
- String oldEditPath = String.format(UploadFilePath.EDIT_PATH, num);
|
|
|
|
- String newEditPath = String.format(UploadFilePath.EDIT_PATH, newNum);
|
|
|
|
- fYunFileServiceInterface.copyFileInBucket(oldEditPath, newEditPath);
|
|
|
|
-
|
|
|
|
- // 拷贝场景展示资源
|
|
|
|
- String oldViewPath = String.format(UploadFilePath.VIEW_PATH, num);
|
|
|
|
- String newViewPath = String.format(UploadFilePath.VIEW_PATH, newNum);
|
|
|
|
- fYunFileServiceInterface.copyFileInBucket(oldViewPath, newViewPath);
|
|
|
|
-
|
|
|
|
- //复制计算结果文件
|
|
|
|
- String oldResultPath = String.format(UploadFilePath.SCENE_RESULT_DATA_PATH, num);
|
|
|
|
- String newResultPath = String.format(UploadFilePath.SCENE_RESULT_DATA_PATH, newNum);
|
|
|
|
- fYunFileServiceInterface.copyFileInBucket(oldResultPath, newResultPath);
|
|
|
|
-
|
|
|
|
- // 拷贝本地资源
|
|
|
|
- String oldPath = String.format("/mnt/4Dkankan/scene/%s/caches/images", num);
|
|
|
|
- String newPath = String.format("/mnt/4Dkankan/scene/%s/caches/images", newNum);
|
|
|
|
- if(new File(oldPath).exists()){
|
|
|
|
- FileUtils.copyDirectiory(oldPath, newPath);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- String scenePath = ConstantFilePath.SCENE_V4_PATH + num;
|
|
|
|
- File file = new File(scenePath);
|
|
|
|
- if(file.exists()){
|
|
|
|
- String newScenePath = ConstantFilePath.SCENE_V4_PATH + newNum;
|
|
|
|
- FileUtils.copyDirectiory(scenePath, newScenePath);
|
|
|
|
- }
|
|
|
|
- String newVideos = plusExt.getVideos();
|
|
|
|
- if(StrUtil.isNotEmpty(newVideos)){
|
|
|
|
- newVideos = plusExt.getVideos().replaceAll("/data/data" + num, "/scene_view_data/" + newNum + "/data").replaceAll(num, newNum);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- String oldDataSource = plusExt.getDataSource();
|
|
|
|
- String newDataSource = sceneProService.setDataSource(plusExt.getDataSource(),time);
|
|
|
|
-
|
|
|
|
- String buildModelPath = ConstantFilePath.BUILD_MODEL_PATH;
|
|
|
|
- if(scenePlus.getSceneSource().equals(4)){
|
|
|
|
- buildModelPath = ConstantFilePath.BUILD_MODEL_LASER_PATH;
|
|
|
|
- }
|
|
|
|
- sceneProService.copyFdage(oldDataSource,newDataSource,buildModelPath,time);
|
|
|
|
-
|
|
|
|
- plusExt.setId(null);
|
|
|
|
- plusExt.setPlusId(scenePlus.getId());
|
|
|
|
- plusExt.setDataSource(newDataSource);
|
|
|
|
- plusExt.setWebSite(plusExt.getWebSite().replace(num, newNum));
|
|
|
|
- plusExt.setThumb(plusExt.getThumb().replace(num, newNum));
|
|
|
|
- plusExt.setVideos(newVideos);
|
|
|
|
- plusExt.setViewCount(0);
|
|
|
|
- scenePlusExtService.save(plusExt);
|
|
|
|
-
|
|
|
|
- SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(plusId);
|
|
|
|
- Long sceneEditInfoId = sceneEditInfo.getId();
|
|
|
|
-
|
|
|
|
- sceneEditInfo.setId(null);
|
|
|
|
- sceneEditInfo.setScenePlusId(scenePlus.getId());
|
|
|
|
- sceneEditInfo.setSceneProId(null);
|
|
|
|
- sceneEditInfo.setTitle(scenePlus.getTitle());
|
|
|
|
- sceneEditInfoService.save(sceneEditInfo);
|
|
|
|
-
|
|
|
|
- SceneEditInfoExt sceneEditInfoExt = sceneEditInfoExtService.getByEditInfoId(sceneEditInfoId);
|
|
|
|
- sceneEditInfoExt.setId(null);
|
|
|
|
- sceneEditInfoExt.setEditInfoId(sceneEditInfo.getId());
|
|
|
|
- sceneEditInfoExt.setScenePlusId(scenePlus.getId());
|
|
|
|
- sceneEditInfoExt.setSceneProId(null);
|
|
|
|
- sceneEditInfoExtService.save(sceneEditInfoExt);
|
|
|
|
-
|
|
|
|
- SceneEditControls sceneEditControls = sceneEditControlsService.getBySceneEditId(sceneEditInfoId);
|
|
|
|
- sceneEditControls.setId(null);
|
|
|
|
- sceneEditControls.setEditInfoId(sceneEditInfo.getId());
|
|
|
|
- sceneEditControlsService.save(sceneEditControls);
|
|
|
|
-
|
|
|
|
- if(scenePlus.getSceneSource() == 4){ //深时复制
|
|
|
|
- laserService.copy(num,scenePlus.getCameraId(),scenePlus.getCreateTime(),newNum,0,null,
|
|
|
|
- sceneEditInfo.getScenePassword(),scenePlus.getTitle(),scenePlus.getUserId(),"V4",plusExt.getIsObj());
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- List<Surveillance> list = surveillanceService.list(new LambdaQueryWrapper<Surveillance>().eq(Surveillance::getNum, num));
|
|
|
|
- if (!Objects.isNull(list)) {
|
|
|
|
- list.stream().forEach(item -> {
|
|
|
|
- item.setNum(newNum);
|
|
|
|
- item.setId(null);
|
|
|
|
- surveillanceService.save(item);
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // 生成scene.json
|
|
|
|
- SceneJsonBean sceneJson = new SceneJsonBean();
|
|
|
|
- BeanUtil.copyProperties(sceneEditInfoExt, sceneJson);
|
|
|
|
- BeanUtil.copyProperties(sceneEditInfo, sceneJson);
|
|
|
|
- SceneEditControlsVO sceneEditControlsVO = BeanUtil.copyProperties(sceneEditControls, SceneEditControlsVO.class);
|
|
|
|
- sceneJson.setControls(sceneEditControlsVO);
|
|
|
|
- sceneJson.setNum(newNum);
|
|
|
|
- sceneJson.setCreateTime(scenePlus.getCreateTime());
|
|
|
|
-
|
|
|
|
- sceneJson.setSceneResolution(plusExt.getSceneResolution());
|
|
|
|
- sceneJson.setSceneFrom(plusExt.getSceneFrom());
|
|
|
|
- sceneJson.setSceneKind(plusExt.getSceneKind());
|
|
|
|
- sceneJson.setModelKind(plusExt.getModelKind());
|
|
|
|
- if(StrUtil.isNotEmpty(plusExt.getVideos())){
|
|
|
|
- sceneJson.setVideos(plusExt.getVideos());
|
|
|
|
- }
|
|
|
|
- sceneJson.setMosaicList(sceneEditInfoService.getMosaicList(num));
|
|
|
|
-
|
|
|
|
- log.info("开始生成本地json文件……");
|
|
|
|
- String sceneJsonLocalPath = ConstantFilePath.SCENE_PATH + "data" + File.separator + "data" + newNum + File.separator + "scene.json";
|
|
|
|
- FileUtils.writeFile(sceneJsonLocalPath, JSON.toJSONString(sceneJson));
|
|
|
|
-
|
|
|
|
- String sceneJsonPath = String.format(UploadFilePath.DATA_VIEW_PATH+"scene.json", newNum);
|
|
|
|
- fYunFileServiceInterface.uploadFile(JSON.toJSONBytes(sceneJson), sceneJsonPath);
|
|
|
|
-
|
|
|
|
- //删除scenejson缓存
|
|
|
|
- redisUtil.del(String.format(RedisKey.SCENE_JSON, num));
|
|
|
|
-
|
|
|
|
- // 生成二维码
|
|
|
|
- String outPathZh = ConstantFilePath.BASE_PATH + File.separator + "sceneQRcode/" + newNum + ".png";
|
|
|
|
- String outPathEn = ConstantFilePath.BASE_PATH + File.separator + "sceneQRcode/" + newNum + "_en.png";
|
|
|
|
- MatrixToImageWriterUtil.createQRCode(plusExt.getWebSite(), outPathZh, false,null);
|
|
|
|
- MatrixToImageWriterUtil.createQRCode(plusExt.getWebSite() + "&lang=en", outPathEn, false, null);
|
|
|
|
- fYunFileServiceInterface.uploadFile(outPathZh, String.format(UploadFilePath.DOWNLOADS_QRCODE, newNum) + newNum + ".png");
|
|
|
|
- fYunFileServiceInterface.uploadFile(outPathEn, String.format(UploadFilePath.DOWNLOADS_QRCODE, newNum) + newNum + "_en.png");
|
|
|
|
- scenePlus.setSceneStatus(-2);
|
|
|
|
- this.updateById(scenePlus);
|
|
|
|
-
|
|
|
|
- cameraDetail.setUsedSpace(cameraDetail.getUsedSpace() + plusExt.getSpace());
|
|
|
|
- cameraDetailService.updateById(cameraDetail);
|
|
|
|
-
|
|
|
|
- if(scenePlus.getPayStatus() == -2){
|
|
|
|
- sceneProService.updateOssStatus(String.format(OssPath.v4_statusPath,scenePlus.getNum()),-2);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- log.info("复制场景结束-{}", new Date());
|
|
|
|
- if(scenePlus.getSceneSource() == 4){ //深时复制
|
|
|
|
- String laserPath = laserService.copyDataSource(oldDataSource,plusExt.getDataSource());
|
|
|
|
- laserService.copy(num,scenePlus.getCameraId(),scenePlus.getCreateTime(),newNum,2,laserPath,
|
|
|
|
- sceneEditInfo.getScenePassword(),scenePlus.getTitle(),scenePlus.getUserId(),"V4",plusExt.getIsObj());
|
|
|
|
- } else if(!"aws".equals(NacosProperty.uploadType)){
|
|
|
|
- laserService.cloudPointBuild(num,newNum);
|
|
|
|
|
|
+ ExecutorService executor = ThreadUtil.newSingleExecutor();
|
|
|
|
+ try {
|
|
|
|
+ CompletableFuture.runAsync(() -> {
|
|
|
|
+ try {
|
|
|
|
+ // 拷贝场景编辑资源
|
|
|
|
+ String oldEditPath = String.format(UploadFilePath.EDIT_PATH, num);
|
|
|
|
+ String newEditPath = String.format(UploadFilePath.EDIT_PATH, newNum);
|
|
|
|
+ fYunFileServiceInterface.copyFileInBucket(oldEditPath, newEditPath);
|
|
|
|
+
|
|
|
|
+ // 拷贝场景展示资源
|
|
|
|
+ String oldViewPath = String.format(UploadFilePath.VIEW_PATH, num);
|
|
|
|
+ String newViewPath = String.format(UploadFilePath.VIEW_PATH, newNum);
|
|
|
|
+ fYunFileServiceInterface.copyFileInBucket(oldViewPath, newViewPath);
|
|
|
|
+
|
|
|
|
+ //复制计算结果文件
|
|
|
|
+ String oldResultPath = String.format(UploadFilePath.SCENE_RESULT_DATA_PATH, num);
|
|
|
|
+ String newResultPath = String.format(UploadFilePath.SCENE_RESULT_DATA_PATH, newNum);
|
|
|
|
+ fYunFileServiceInterface.copyFileInBucket(oldResultPath, newResultPath);
|
|
|
|
+
|
|
|
|
+ // 拷贝本地资源
|
|
|
|
+ String oldPath = String.format("/mnt/4Dkankan/scene/%s/caches/images", num);
|
|
|
|
+ String newPath = String.format("/mnt/4Dkankan/scene/%s/caches/images", newNum);
|
|
|
|
+ if(new File(oldPath).exists()){
|
|
|
|
+ FileUtils.copyDirectiory(oldPath, newPath);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ String scenePath = ConstantFilePath.SCENE_V4_PATH + num;
|
|
|
|
+ File file = new File(scenePath);
|
|
|
|
+ if(file.exists()){
|
|
|
|
+ String newScenePath = ConstantFilePath.SCENE_V4_PATH + newNum;
|
|
|
|
+ FileUtils.copyDirectiory(scenePath, newScenePath);
|
|
|
|
+ }
|
|
|
|
+ String newVideos = plusExt.getVideos();
|
|
|
|
+ if(StrUtil.isNotEmpty(newVideos)){
|
|
|
|
+ newVideos = plusExt.getVideos().replaceAll("/data/data" + num, "/scene_view_data/" + newNum + "/data").replaceAll(num, newNum);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ String oldDataSource = plusExt.getDataSource();
|
|
|
|
+ String newDataSource = sceneProService.setDataSource(plusExt.getDataSource(),time);
|
|
|
|
+
|
|
|
|
+ String buildModelPath = ConstantFilePath.BUILD_MODEL_PATH;
|
|
|
|
+ if(scenePlus.getSceneSource().equals(4)){
|
|
|
|
+ buildModelPath = ConstantFilePath.BUILD_MODEL_LASER_PATH;
|
|
|
|
+ }
|
|
|
|
+ sceneProService.copyFdage(oldDataSource,newDataSource,buildModelPath,time);
|
|
|
|
+
|
|
|
|
+ plusExt.setId(null);
|
|
|
|
+ plusExt.setPlusId(scenePlus.getId());
|
|
|
|
+ plusExt.setDataSource(newDataSource);
|
|
|
|
+ plusExt.setWebSite(plusExt.getWebSite().replace(num, newNum));
|
|
|
|
+ plusExt.setThumb(plusExt.getThumb().replace(num, newNum));
|
|
|
|
+ plusExt.setVideos(newVideos);
|
|
|
|
+ plusExt.setViewCount(0);
|
|
|
|
+ scenePlusExtService.save(plusExt);
|
|
|
|
+
|
|
|
|
+ SceneEditInfo sceneEditInfo = sceneEditInfoService.getByScenePlusId(plusId);
|
|
|
|
+ Long sceneEditInfoId = sceneEditInfo.getId();
|
|
|
|
+
|
|
|
|
+ sceneEditInfo.setId(null);
|
|
|
|
+ sceneEditInfo.setScenePlusId(scenePlus.getId());
|
|
|
|
+ sceneEditInfo.setSceneProId(null);
|
|
|
|
+ sceneEditInfo.setTitle(scenePlus.getTitle());
|
|
|
|
+ sceneEditInfoService.save(sceneEditInfo);
|
|
|
|
+
|
|
|
|
+ SceneEditInfoExt sceneEditInfoExt = sceneEditInfoExtService.getByEditInfoId(sceneEditInfoId);
|
|
|
|
+ sceneEditInfoExt.setId(null);
|
|
|
|
+ sceneEditInfoExt.setEditInfoId(sceneEditInfo.getId());
|
|
|
|
+ sceneEditInfoExt.setScenePlusId(scenePlus.getId());
|
|
|
|
+ sceneEditInfoExt.setSceneProId(null);
|
|
|
|
+ sceneEditInfoExtService.save(sceneEditInfoExt);
|
|
|
|
+
|
|
|
|
+ SceneEditControls sceneEditControls = sceneEditControlsService.getBySceneEditId(sceneEditInfoId);
|
|
|
|
+ sceneEditControls.setId(null);
|
|
|
|
+ sceneEditControls.setEditInfoId(sceneEditInfo.getId());
|
|
|
|
+ sceneEditControlsService.save(sceneEditControls);
|
|
|
|
+
|
|
|
|
+ if(scenePlus.getSceneSource() == 4){ //深时复制
|
|
|
|
+ laserService.copy(num,scenePlus.getCameraId(),scenePlus.getCreateTime(),newNum,0,null,
|
|
|
|
+ sceneEditInfo.getScenePassword(),scenePlus.getTitle(),scenePlus.getUserId(),"V4",plusExt.getIsObj());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ List<Surveillance> list = surveillanceService.list(new LambdaQueryWrapper<Surveillance>().eq(Surveillance::getNum, num));
|
|
|
|
+ if (!Objects.isNull(list)) {
|
|
|
|
+ list.stream().forEach(item -> {
|
|
|
|
+ item.setNum(newNum);
|
|
|
|
+ item.setId(null);
|
|
|
|
+ surveillanceService.save(item);
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 生成scene.json
|
|
|
|
+ SceneJsonBean sceneJson = new SceneJsonBean();
|
|
|
|
+ BeanUtil.copyProperties(sceneEditInfoExt, sceneJson);
|
|
|
|
+ BeanUtil.copyProperties(sceneEditInfo, sceneJson);
|
|
|
|
+ SceneEditControlsVO sceneEditControlsVO = BeanUtil.copyProperties(sceneEditControls, SceneEditControlsVO.class);
|
|
|
|
+ sceneJson.setControls(sceneEditControlsVO);
|
|
|
|
+ sceneJson.setNum(newNum);
|
|
|
|
+ sceneJson.setCreateTime(scenePlus.getCreateTime());
|
|
|
|
+
|
|
|
|
+ sceneJson.setSceneResolution(plusExt.getSceneResolution());
|
|
|
|
+ sceneJson.setSceneFrom(plusExt.getSceneFrom());
|
|
|
|
+ sceneJson.setSceneKind(plusExt.getSceneKind());
|
|
|
|
+ sceneJson.setModelKind(plusExt.getModelKind());
|
|
|
|
+ if(StrUtil.isNotEmpty(plusExt.getVideos())){
|
|
|
|
+ sceneJson.setVideos(plusExt.getVideos());
|
|
|
|
+ }
|
|
|
|
+ sceneJson.setMosaicList(sceneEditInfoService.getMosaicList(num));
|
|
|
|
+
|
|
|
|
+ log.info("开始生成本地json文件……");
|
|
|
|
+ String sceneJsonLocalPath = ConstantFilePath.SCENE_PATH + "data" + File.separator + "data" + newNum + File.separator + "scene.json";
|
|
|
|
+ FileUtils.writeFile(sceneJsonLocalPath, JSON.toJSONString(sceneJson));
|
|
|
|
+
|
|
|
|
+ String sceneJsonPath = String.format(UploadFilePath.DATA_VIEW_PATH+"scene.json", newNum);
|
|
|
|
+ fYunFileServiceInterface.uploadFile(JSON.toJSONBytes(sceneJson), sceneJsonPath);
|
|
|
|
+
|
|
|
|
+ //删除scenejson缓存
|
|
|
|
+ redisUtil.del(String.format(RedisKey.SCENE_JSON, num));
|
|
|
|
+
|
|
|
|
+ // 生成二维码
|
|
|
|
+ String outPathZh = ConstantFilePath.BASE_PATH + File.separator + "sceneQRcode/" + newNum + ".png";
|
|
|
|
+ String outPathEn = ConstantFilePath.BASE_PATH + File.separator + "sceneQRcode/" + newNum + "_en.png";
|
|
|
|
+ MatrixToImageWriterUtil.createQRCode(plusExt.getWebSite(), outPathZh, false,null);
|
|
|
|
+ MatrixToImageWriterUtil.createQRCode(plusExt.getWebSite() + "&lang=en", outPathEn, false, null);
|
|
|
|
+ fYunFileServiceInterface.uploadFile(outPathZh, String.format(UploadFilePath.DOWNLOADS_QRCODE, newNum) + newNum + ".png");
|
|
|
|
+ fYunFileServiceInterface.uploadFile(outPathEn, String.format(UploadFilePath.DOWNLOADS_QRCODE, newNum) + newNum + "_en.png");
|
|
|
|
+ scenePlus.setSceneStatus(-2);
|
|
|
|
+ this.updateById(scenePlus);
|
|
|
|
+
|
|
|
|
+ cameraDetail.setUsedSpace(cameraDetail.getUsedSpace() + plusExt.getSpace());
|
|
|
|
+ cameraDetailService.updateById(cameraDetail);
|
|
|
|
+
|
|
|
|
+ if(scenePlus.getPayStatus() == -2){
|
|
|
|
+ sceneProService.updateOssStatus(String.format(OssPath.v4_statusPath,scenePlus.getNum()),-2);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(scenePlus.getSceneSource() == 4){ //深时复制
|
|
|
|
+ String laserPath = laserService.copyDataSource(oldDataSource,plusExt.getDataSource());
|
|
|
|
+ laserService.copy(num,scenePlus.getCameraId(),scenePlus.getCreateTime(),newNum,2,laserPath,
|
|
|
|
+ sceneEditInfo.getScenePassword(),scenePlus.getTitle(),scenePlus.getUserId(),"V4",plusExt.getIsObj());
|
|
|
|
+ } else if(!"aws".equals(NacosProperty.uploadType)){
|
|
|
|
+ laserService.cloudPointBuild(num,newNum);
|
|
|
|
+ }
|
|
|
|
+ sceneCopyLogService.saveByNum(num,newNum,scenePlus.getUserId());
|
|
|
|
+
|
|
|
|
+ }catch (Exception e){
|
|
|
|
+ this.removeById(scenePlus.getId());
|
|
|
|
+ log.error("复制场景异常", e);
|
|
}
|
|
}
|
|
- sceneCopyLogService.saveByNum(num,newNum,scenePlus.getUserId());
|
|
|
|
|
|
+ }, executor).whenComplete((reslut, e) -> {
|
|
|
|
+ log.info("复制场景oldNum:{},newNum:{}结束-{}",num,newNum, new Date());
|
|
|
|
+ });
|
|
|
|
+ }catch (Exception e){
|
|
|
|
+ log.error("线程错误:{}",e);
|
|
|
|
+ }finally {
|
|
|
|
+ executor.shutdown();
|
|
|
|
+ }
|
|
|
|
|
|
- }catch (Exception e){
|
|
|
|
- this.removeById(scenePlus.getId());
|
|
|
|
- log.error("复制场景异常", e);
|
|
|
|
- }
|
|
|
|
- }).join();
|
|
|
|
return scenePlus.getId();
|
|
return scenePlus.getId();
|
|
}
|
|
}
|
|
|
|
|