Merge branch 'main' into dev_20260130_RemoveRedis
# Conflicts: # backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/jm/task/JmTaskScheduler.java # backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/control/SarControlContext.java # backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/listen/SarStatusListener.java # backend/Skyeye-sys-dev/skyeye-service-manager/src/main/java/com/zhangy/skyeye/sar/service/impl/SarControlServiceImpl.java
This commit is contained in:
commit
0b30444512
@ -28,7 +28,6 @@ import com.zhangy.skyeye.publics.consts.UavAirlineUploadEnum;
|
|||||||
import com.zhangy.skyeye.publics.consts.WebSocketKey;
|
import com.zhangy.skyeye.publics.consts.WebSocketKey;
|
||||||
import com.zhangy.skyeye.publics.service.SysFileTypeService;
|
import com.zhangy.skyeye.publics.service.SysFileTypeService;
|
||||||
import com.zhangy.skyeye.publics.utils.CoordUtil;
|
import com.zhangy.skyeye.publics.utils.CoordUtil;
|
||||||
import com.zhangy.skyeye.py.service.IPyAirlineService;
|
|
||||||
import com.zhangy.skyeye.quartz.service.QuartzService;
|
import com.zhangy.skyeye.quartz.service.QuartzService;
|
||||||
import com.zhangy.skyeye.sar.consts.SarImageModeEnum;
|
import com.zhangy.skyeye.sar.consts.SarImageModeEnum;
|
||||||
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
|
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
|
||||||
@ -353,7 +352,7 @@ public class JmJobServiceImpl implements JmJobService {
|
|||||||
|
|
||||||
JmJobUav uav = job.getUavList().get(0);
|
JmJobUav uav = job.getUavList().get(0);
|
||||||
List<JmAirline> airlineList = uav.getAirlineList();
|
List<JmAirline> airlineList = uav.getAirlineList();
|
||||||
for(JmAirline airline:airlineList) {
|
for (JmAirline airline : airlineList) {
|
||||||
log.info("we are going to start job {}", job.getId());
|
log.info("we are going to start job {}", job.getId());
|
||||||
log.info("airline origin status is {}, set to {}", airline.getStatus(), ExecStatusEnum.NOT);
|
log.info("airline origin status is {}, set to {}", airline.getStatus(), ExecStatusEnum.NOT);
|
||||||
airline.setStatus(ExecStatusEnum.NOT.getValue());
|
airline.setStatus(ExecStatusEnum.NOT.getValue());
|
||||||
@ -377,6 +376,7 @@ public class JmJobServiceImpl implements JmJobService {
|
|||||||
// 4.任务状态放入缓存
|
// 4.任务状态放入缓存
|
||||||
jobStatusService.add(job);
|
jobStatusService.add(job);
|
||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
|
log.warn("{}", ex.getMessage(), ex);
|
||||||
jobStatusService.remove(job.getId());
|
jobStatusService.remove(job.getId());
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -78,9 +78,11 @@ public class SarControlContext {
|
|||||||
* @param param
|
* @param param
|
||||||
*/
|
*/
|
||||||
public void execute(SarControlParamDTO param) {
|
public void execute(SarControlParamDTO param) {
|
||||||
|
log.info("execute :{}", JSON.toJSONString(param));
|
||||||
List<ISarControlStrategy> matchedStrategys = strategies.stream()
|
List<ISarControlStrategy> matchedStrategys = strategies.stream()
|
||||||
.filter(strategy -> strategy.supports(param))
|
.filter(strategy -> strategy.supports(param))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
log.info("matchedStrategys :{}", JSON.toJSONString(matchedStrategys));
|
||||||
if (ObjectUtil.isEmpty(matchedStrategys)) {
|
if (ObjectUtil.isEmpty(matchedStrategys)) {
|
||||||
throw ServiceException.errorLog("无效的控制指令!");
|
throw ServiceException.errorLog("无效的控制指令!");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -75,11 +75,10 @@ public class SarStatusListener extends SarAbstractListener {
|
|||||||
if (packet.getLength() != 100) {
|
if (packet.getLength() != 100) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.debug("接收到状态包----------------------");
|
|
||||||
String ip = packet.getAddress().getHostAddress();
|
String ip = packet.getAddress().getHostAddress();
|
||||||
// 处理接收到的数据
|
// 处理接收到的数据
|
||||||
SarStatusPackDTO packDTO = SarStatusPackDTO.parse(ip, packet.getData());
|
SarStatusPackDTO packDTO = SarStatusPackDTO.parse(ip, packet.getData());
|
||||||
log.debug("recv status:\n{}", JSON.toJSONString(packDTO));
|
log.trace("recv status:\n{}", JSON.toJSONString(packDTO));
|
||||||
if (packDTO == null) {
|
if (packDTO == null) {
|
||||||
if (running)
|
if (running)
|
||||||
log.warn("状态包校验失败,已丢弃。");
|
log.warn("状态包校验失败,已丢弃。");
|
||||||
@ -102,6 +101,5 @@ public class SarStatusListener extends SarAbstractListener {
|
|||||||
sarJobStatusService.update(ip, info);
|
sarJobStatusService.update(ip, info);
|
||||||
sarCache.saveStatus(ip, info); // 使用 SarCache,类型安全,自动短期缓存,自动过期
|
sarCache.saveStatus(ip, info); // 使用 SarCache,类型安全,自动短期缓存,自动过期
|
||||||
}
|
}
|
||||||
log.debug("状态包解析完毕");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,11 +1,12 @@
|
|||||||
package com.zhangy.skyeye.sar.service.impl;
|
package com.zhangy.skyeye.sar.service.impl;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.zhangy.skyeye.cache.sar.SarCache;
|
||||||
import com.zhangy.skyeye.common.extend.exception.ServiceException;
|
import com.zhangy.skyeye.common.extend.exception.ServiceException;
|
||||||
import com.zhangy.skyeye.common.extend.util.JsonUtil;
|
import com.zhangy.skyeye.common.extend.util.JsonUtil;
|
||||||
import com.zhangy.skyeye.jm.dto.JmJobDTO;
|
import com.zhangy.skyeye.jm.dto.JmJobDTO;
|
||||||
import com.zhangy.skyeye.jm.dto.JmSarStatusDTO;
|
import com.zhangy.skyeye.jm.dto.JmSarStatusDTO;
|
||||||
import com.zhangy.skyeye.jm.entity.JmJobPayload;
|
import com.zhangy.skyeye.jm.entity.JmJobPayload;
|
||||||
import com.zhangy.skyeye.cache.sar.SarCache;
|
|
||||||
import com.zhangy.skyeye.sar.control.SarControlContext;
|
import com.zhangy.skyeye.sar.control.SarControlContext;
|
||||||
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
|
import com.zhangy.skyeye.sar.dto.SarControlParamDTO;
|
||||||
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
|
import com.zhangy.skyeye.sar.enums.SarControlTypeEnum;
|
||||||
@ -31,6 +32,7 @@ public class SarControlServiceImpl implements ISarControlService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendUdp(JmJobDTO job) {
|
public void sendUdp(JmJobDTO job) {
|
||||||
|
log.info("sendUdp:{}", JSON.toJSONString(job));
|
||||||
if (job == null || job.getUavList() == null) return;
|
if (job == null || job.getUavList() == null) return;
|
||||||
job.getUavList().forEach(uav -> {
|
job.getUavList().forEach(uav -> {
|
||||||
JmJobPayload sar = uav.getSar0();
|
JmJobPayload sar = uav.getSar0();
|
||||||
@ -43,7 +45,7 @@ public class SarControlServiceImpl implements ISarControlService {
|
|||||||
param.setImageBit(sar.getImageBit());
|
param.setImageBit(sar.getImageBit());
|
||||||
param.setResolution(sar.getResolution());
|
param.setResolution(sar.getResolution());
|
||||||
if (job.getMode().equals(CRUISE.getCode())) {
|
if (job.getMode().equals(CRUISE.getCode())) {
|
||||||
param.setControlType(SarControlTypeEnum.TURNON);
|
param.setControlType(SarControlTypeEnum.UPLOAD);
|
||||||
} else {
|
} else {
|
||||||
param.setControlType(SarControlTypeEnum.AUTO);
|
param.setControlType(SarControlTypeEnum.AUTO);
|
||||||
}
|
}
|
||||||
@ -62,6 +64,7 @@ public class SarControlServiceImpl implements ISarControlService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void sendUdp(SarControlParamDTO param) {
|
public void sendUdp(SarControlParamDTO param) {
|
||||||
udpSendContext.execute(param);
|
udpSendContext.execute(param);
|
||||||
|
|||||||
@ -1151,7 +1151,7 @@ export default {
|
|||||||
})
|
})
|
||||||
let airlineList = [
|
let airlineList = [
|
||||||
{
|
{
|
||||||
direction: 0,
|
direction: params.direction,
|
||||||
distance: 0,
|
distance: 0,
|
||||||
endHeight: 0,
|
endHeight: 0,
|
||||||
endLat: 0,
|
endLat: 0,
|
||||||
@ -1165,7 +1165,7 @@ export default {
|
|||||||
flightStartLat: 0,
|
flightStartLat: 0,
|
||||||
flightStartLon: 0,
|
flightStartLon: 0,
|
||||||
flightType: 0,
|
flightType: 0,
|
||||||
grazingAngle: 0,
|
grazingAngle: 90 - params.theta,
|
||||||
groundStartHeight: 0,
|
groundStartHeight: 0,
|
||||||
groundStartLat: 0,
|
groundStartLat: 0,
|
||||||
groundStartLon: 0,
|
groundStartLon: 0,
|
||||||
|
|||||||
@ -11,7 +11,7 @@ export default class OrthoImageryManager {
|
|||||||
this.maxLayers = options.maxLayers || 400;
|
this.maxLayers = options.maxLayers || 400;
|
||||||
|
|
||||||
// 全局亮度与透明度
|
// 全局亮度与透明度
|
||||||
this.globalBrightness = options.brightness ?? 1.0;
|
this.globalBrightness = options.brightness ?? 2;
|
||||||
this.globalAlpha = options.alpha ?? 1.0;
|
this.globalAlpha = options.alpha ?? 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ export default class OrthoImageryManager {
|
|||||||
const layer = this.viewer.cesiumViewer.imageryLayers.addImageryProvider(provider);
|
const layer = this.viewer.cesiumViewer.imageryLayers.addImageryProvider(provider);
|
||||||
|
|
||||||
// ✅ 使用全局亮度和透明度,保证新图生效
|
// ✅ 使用全局亮度和透明度,保证新图生效
|
||||||
layer.brightness = brightness ? brightness : this.globalBrightness;
|
layer.brightness = this.globalBrightness;
|
||||||
console.log(layer.brightness, '新增图片时的亮度值');
|
console.log(layer.brightness, '新增图片时的亮度值');
|
||||||
|
|
||||||
layer.alpha = this.globalAlpha;
|
layer.alpha = this.globalAlpha;
|
||||||
@ -54,7 +54,7 @@ export default class OrthoImageryManager {
|
|||||||
* 设置全局亮度,影响所有已有图片和未来新推送的图片
|
* 设置全局亮度,影响所有已有图片和未来新推送的图片
|
||||||
*/
|
*/
|
||||||
setBrightness(value) {
|
setBrightness(value) {
|
||||||
// this.globalBrightness = value;
|
this.globalBrightness = value;
|
||||||
this.layerMap.forEach(layer => {
|
this.layerMap.forEach(layer => {
|
||||||
layer.brightness = value;
|
layer.brightness = value;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -54,6 +54,7 @@ let sceneEntity = {}
|
|||||||
let taskListResource = []
|
let taskListResource = []
|
||||||
let orthoManager = null
|
let orthoManager = null
|
||||||
let socketPositionIndex = 0
|
let socketPositionIndex = 0
|
||||||
|
let uavTracks = {}
|
||||||
const lang = window.localStorage.getItem('locale') || 'zh_CN'
|
const lang = window.localStorage.getItem('locale') || 'zh_CN'
|
||||||
export default {
|
export default {
|
||||||
name: 'TwinSituation',
|
name: 'TwinSituation',
|
||||||
@ -472,7 +473,7 @@ export default {
|
|||||||
// )
|
// )
|
||||||
orthoManager = new OrthoImageryManager(viewer, DT.Cesium, {
|
orthoManager = new OrthoImageryManager(viewer, DT.Cesium, {
|
||||||
maxLayers: 400,
|
maxLayers: 400,
|
||||||
brightness: 1, // 默认亮度
|
brightness: 5, // 默认亮度
|
||||||
alpha: 1.0 // 默认透明度
|
alpha: 1.0 // 默认透明度
|
||||||
})
|
})
|
||||||
// this.startTest()
|
// this.startTest()
|
||||||
@ -481,6 +482,7 @@ export default {
|
|||||||
this.SET_SPLIT_VISIBLE(false)
|
this.SET_SPLIT_VISIBLE(false)
|
||||||
this.removePictureHandle()
|
this.removePictureHandle()
|
||||||
socketPositionIndex = 0
|
socketPositionIndex = 0
|
||||||
|
uavTracks = {}
|
||||||
if (orthoManager) {
|
if (orthoManager) {
|
||||||
orthoManager.clearAll()
|
orthoManager.clearAll()
|
||||||
orthoManager = null
|
orthoManager = null
|
||||||
@ -1099,6 +1101,7 @@ export default {
|
|||||||
let data = res.data.data
|
let data = res.data.data
|
||||||
let list = []
|
let list = []
|
||||||
socketPositionIndex = 0
|
socketPositionIndex = 0
|
||||||
|
uavTracks = {}
|
||||||
data.forEach(item => {
|
data.forEach(item => {
|
||||||
// item.check = item.status === 1 || item.status === 3
|
// item.check = item.status === 1 || item.status === 3
|
||||||
item.check = false
|
item.check = false
|
||||||
@ -1118,7 +1121,7 @@ export default {
|
|||||||
uav = item.uavList[0].uavName
|
uav = item.uavList[0].uavName
|
||||||
if (item.uavList[0].payloadList.length) {
|
if (item.uavList[0].payloadList.length) {
|
||||||
sar = item.uavList[0].payloadList[0].payloadName
|
sar = item.uavList[0].payloadList[0].payloadName
|
||||||
ip = item.uavList[0].ip
|
ip = item.uavList[0].payloadList[0].ip
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
list.push({
|
list.push({
|
||||||
@ -1582,6 +1585,7 @@ export default {
|
|||||||
// }
|
// }
|
||||||
this.removeTaskTarget(info.id);
|
this.removeTaskTarget(info.id);
|
||||||
socketPositionIndex = 0
|
socketPositionIndex = 0
|
||||||
|
uavTracks = {}
|
||||||
if (orthoManager) {
|
if (orthoManager) {
|
||||||
orthoManager.clearAll()
|
orthoManager.clearAll()
|
||||||
orthoManager = null
|
orthoManager = null
|
||||||
@ -1614,7 +1618,9 @@ export default {
|
|||||||
viewer.entities.remove(item)
|
viewer.entities.remove(item)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
viewer.entities
|
||||||
socketPositionIndex = 0
|
socketPositionIndex = 0
|
||||||
|
uavTracks = {}
|
||||||
if (orthoManager) {
|
if (orthoManager) {
|
||||||
orthoManager.clearAll()
|
orthoManager.clearAll()
|
||||||
orthoManager = null
|
orthoManager = null
|
||||||
@ -2786,51 +2792,76 @@ export default {
|
|||||||
this.updateSceneUav(data)
|
this.updateSceneUav(data)
|
||||||
},
|
},
|
||||||
updateSceneUav(data) {
|
updateSceneUav(data) {
|
||||||
console.log(data, 44444);
|
// 1. 坐标转换(建议添加异常处理,避免非法坐标)
|
||||||
|
if (!data.longitude || !data.latitude) {
|
||||||
|
console.warn('无人机坐标不完整', data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let position = DT.Cesium.Cartesian3.fromDegrees(
|
||||||
|
Number(data.longitude),
|
||||||
|
Number(data.latitude),
|
||||||
|
Number(data.altitude) || 0 // 高度默认0,避免undefined
|
||||||
|
);
|
||||||
|
let uavId = 'uav-' + data.jobId;
|
||||||
|
|
||||||
let position = DT.Cesium.Cartesian3.fromDegrees(data.longitude, data.latitude, data.altitude)
|
// 2. 轨迹数组维护(过滤重复坐标,避免无长度轨迹)
|
||||||
let entity = viewer.entities.getById('uav-' + data.id)
|
if (uavTracks[uavId]) {
|
||||||
|
// 获取最后一个坐标,计算与新坐标的距离(小于1米则不添加)
|
||||||
|
const lastPos = uavTracks[uavId][uavTracks[uavId].length - 1];
|
||||||
|
const distance = lastPos ? DT.Cesium.Cartesian3.distance(lastPos, position) : 0;
|
||||||
|
// 只有距离大于1米时才添加新坐标,避免高频重复
|
||||||
|
if (distance > 1) {
|
||||||
|
uavTracks[uavId].push(position);
|
||||||
|
// 限制轨迹长度,避免数组过大(可选,比如最多保存1000个点)
|
||||||
|
if (uavTracks[uavId].length > 1000) {
|
||||||
|
uavTracks[uavId].shift();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uavTracks[uavId] = [position];
|
||||||
|
}
|
||||||
|
// 3. 获取/创建无人机实体
|
||||||
|
let entity = viewer.entities.getById(uavId);
|
||||||
if (entity) {
|
if (entity) {
|
||||||
entity.position = position
|
entity.position = position;
|
||||||
} else {
|
} else {
|
||||||
entity = viewer.entities.add({
|
entity = viewer.entities.add({
|
||||||
id: 'uav-' + data.id,
|
id: uavId,
|
||||||
position: position,
|
position: position,
|
||||||
label: {
|
label: {
|
||||||
text: '无人机',
|
text: '无人机',
|
||||||
font: '14px sans-serif',
|
font: '14px sans-serif',
|
||||||
fillColor: DT.Cesium.Color.WHITE,
|
fillColor: DT.Cesium.Color.RED,
|
||||||
horizontalOrigin: DT.Cesium.HorizontalOrigin.LEFT,
|
horizontalOrigin: DT.Cesium.HorizontalOrigin.LEFT,
|
||||||
verticalOrigin: DT.Cesium.VerticalOrigin.CENTER,
|
verticalOrigin: DT.Cesium.VerticalOrigin.CENTER,
|
||||||
disableDepthTestDistance: 100000,
|
disableDepthTestDistance: 100000,
|
||||||
pixelOffset: new DT.Cesium.Cartesian2(10, 0),
|
|
||||||
showBackground: true,
|
showBackground: true,
|
||||||
scaleByDistance: new DT.Cesium.NearFarScalar(0, 1, 1, 0.8),
|
scaleByDistance: new DT.Cesium.NearFarScalar(0, 1, 1, 0.8),
|
||||||
distanceDisplayCondition: new DT.Cesium.DistanceDisplayCondition(0, 20000),
|
distanceDisplayCondition: new DT.Cesium.DistanceDisplayCondition(0, 10000),
|
||||||
backgroundColor: DT.Cesium.Color.fromCssColorString('rgba(0,0,0,0.7)'),
|
backgroundColor: DT.Cesium.Color.fromCssColorString('rgba(255,255,255,0.6)'),
|
||||||
style: DT.Cesium.LabelStyle.FILL_AND_OUTLINE,
|
pixelOffset: new DT.Cesium.Cartesian2(10, -15),
|
||||||
},
|
},
|
||||||
model: {
|
model: {
|
||||||
uri: process.env.BASE_URL + 'model/uav.gltf',
|
uri: process.env.BASE_URL + 'model/uav.gltf',
|
||||||
minimumPixelSize: 64,
|
minimumPixelSize: 64,
|
||||||
maximumScale: 128,
|
maximumScale: 128,
|
||||||
},
|
},
|
||||||
// point: {
|
|
||||||
// pixelSize: 40,
|
|
||||||
// color: DT.Cesium.Color.RED,
|
|
||||||
// disableDepthTestDistance: 10000
|
|
||||||
// },
|
|
||||||
polyline: {
|
polyline: {
|
||||||
positions: new DT.Cesium.CallbackProperty(function () {
|
// 修复:绑定uavId到回调作用域,避免丢失
|
||||||
return this.positions
|
positions: new DT.Cesium.CallbackProperty(() => {
|
||||||
|
return uavTracks[uavId] || [];
|
||||||
}, false),
|
}, false),
|
||||||
width: 8,
|
width: 2, // 线宽加大,更容易观察
|
||||||
zIndex: 600,
|
zIndex: 600, // 提高层级,避免被其他元素覆盖
|
||||||
material: DT.Cesium.Color.BLACK,
|
material: DT.Cesium.Color.fromCssColorString('#1fe46b'), // 半透明,更易见
|
||||||
depthFailMaterial: DT.Cesium.Color.CYAN,
|
depthFailMaterial: DT.Cesium.Color.CYAN.withAlpha(0.8),
|
||||||
arcType: DT.Cesium.ArcType.NONE
|
arcType: DT.Cesium.ArcType.NONE, // 关键:添加直线轨迹配置
|
||||||
|
disableDepthTestDistance: Number.POSITIVE_INFINITY, // 永不被遮挡
|
||||||
|
distanceDisplayCondition: new DT.Cesium.DistanceDisplayCondition(0, 50000), // 扩大显示范围
|
||||||
|
clampToGround: false, // 不贴地,跟随无人机高度
|
||||||
|
show: true // 显式开启,避免默认隐藏
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleWebsocketWave(info) {
|
handleWebsocketWave(info) {
|
||||||
@ -2870,7 +2901,7 @@ export default {
|
|||||||
// this.imageInfos = Object.assign({}, this.imageInfos, data)
|
// this.imageInfos = Object.assign({}, this.imageInfos, data)
|
||||||
// this.imageInfos.brightness = data.brightness
|
// this.imageInfos.brightness = data.brightness
|
||||||
console.log('SAR图像2', data)
|
console.log('SAR图像2', data)
|
||||||
orthoManager.add(data, this.imageInfos.brightness)
|
orthoManager.add(data)
|
||||||
// this.addMarkPicture2(data)
|
// this.addMarkPicture2(data)
|
||||||
// console.log('灭有匹配到吗taskUavCollection', taskUavCollection, taskUavCollection[data.jobId])
|
// console.log('灭有匹配到吗taskUavCollection', taskUavCollection, taskUavCollection[data.jobId])
|
||||||
// if (taskUavCollection[data.jobId]) {
|
// if (taskUavCollection[data.jobId]) {
|
||||||
@ -3090,6 +3121,7 @@ export default {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
socketPositionIndex = 0
|
socketPositionIndex = 0
|
||||||
|
uavTracks = {}
|
||||||
if (orthoManager) {
|
if (orthoManager) {
|
||||||
orthoManager.clearAll()
|
orthoManager.clearAll()
|
||||||
orthoManager = null
|
orthoManager = null
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user