liusuyi
2026-05-30 f4f4fc53260eb67483dce406a963628273786a61
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
package com.ard.work.sdk.service;
 
import com.ard.common.core.constant.CacheConstants;
import com.ard.common.core.exception.ServiceException;
import com.ard.common.core.exception.device.CameraSDKException;
import com.ard.common.core.web.domain.AjaxResult;
import com.ard.common.redis.service.RedisService;
import com.ard.common.security.utils.SecurityUtils;
import com.ard.work.api.domian.*;
import com.ard.work.device.camera.domain.PtzParamDTO;
import com.ard.work.device.noview.domain.ArdNoViewingZone;
import com.ard.work.device.noview.service.IArdNoViewingZoneService;
import com.ard.work.sdk.annotation.SdkOperate;
import com.ard.work.sdk.common.RecordManager;
import com.ard.work.sdk.factory.CameraSDKFactory;
import com.ard.work.sdk.model.DeviceCFG;
import com.ard.work.sdk.model.PtzScope;
import com.ard.work.utils.gis.GisUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
 
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
 
@Service
@Slf4j
public class CameraSDKService {
 
    @Resource
    private RedisService redisService;
    @Resource
    private CameraSDKFactory cameraSDKFactory;
    @Resource
    private IArdNoViewingZoneService ardNoViewingZoneService;
 
    // ========== 基础操作 ==========
 
    @Async("cameraSdkExecutor")
    public void login(ArdCamera ardCamera) {
        if (ardCamera == null) {
            throw CameraSDKException.cameraNotExist(null);
        }
        CameraSDK sdk = cameraSDKFactory.createCameraSDK(ardCamera.getFactory());
        sdk.login(ardCamera);
    }
 
    public void logout(String cameraId) {
        ArdCamera ardCamera = redisService.getCacheMapValue(CacheConstants.CAMERA_LIST, cameraId);
        if (ardCamera == null || ardCamera.getFactory() == null) {
            return;
        }
        CameraSDK sdk = cameraSDKFactory.createCameraSDK(ardCamera.getFactory());
        sdk.logout(cameraId);
    }
 
    // ========== 简单操作(一行搞定) ==========
 
    @SdkOperate
    public Boolean pTZControl(CameraCmd cmd) {
        return getSdk(cmd).pTZControl(cmd);
    }
 
    public Boolean setFocusPos(CameraCmd cmd) {
        return getSdk(cmd).setFocusPos(cmd);
    }
 
    public Integer getFocusPos(CameraCmd cmd) {
        return getSdk(cmd).getFocusPos(cmd);
    }
 
    public Boolean setPreset(CameraCmd cmd) {
        return getSdk(cmd).setPreset(cmd);
    }
 
    public Boolean gotoPreset(CameraCmd cmd) {
        return getSdk(cmd).gotoPreset(cmd);
    }
 
    public VideoCompressionCfg getVideoCompressionCfg(CameraCmd cmd) {
        return getSdk(cmd).getVideoCompressionCfg(cmd);
    }
 
    public Boolean controlDefog(CameraCmd cmd) {
        return getSdk(cmd).controlDefog(cmd);
    }
 
    public Boolean isEnableDefog(CameraCmd cmd) {
        return getSdk(cmd).isEnableDefog(cmd);
    }
 
    public Boolean controlInfrared(CameraCmd cmd) {
        return getSdk(cmd).controlInfrared(cmd);
    }
 
    public Boolean isEnableInfrared(CameraCmd cmd) {
        return getSdk(cmd).isEnableInfrared(cmd);
    }
 
    public Boolean controlFocusMode(CameraCmd cmd) {
        return getSdk(cmd).controlFocusMode(cmd);
    }
 
    public Boolean getFocusMode(CameraCmd cmd) {
        return getSdk(cmd).getFocusMode(cmd);
    }
 
    public Boolean controlPTHeater(CameraCmd cmd) {
        return getSdk(cmd).controlPTHeater(cmd);
    }
 
    public Boolean controlCameraDeicing(CameraCmd cmd) {
        return getSdk(cmd).controlCameraDeicing(cmd);
    }
 
    public Boolean getPTZLockInfo(CameraCmd cmd) {
        return getSdk(cmd).getPTZLockInfo(cmd);
    }
 
    public Boolean setPTZLockInfo(CameraCmd cmd) {
        return getSdk(cmd).setPTZLockInfo(cmd);
    }
 
    public byte[] localCapture(CameraCmd cmd) {
        return getSdk(cmd).localCapture(cmd);
    }
 
    public PtzScope getPtzScope(CameraCmd cmd) {
        return getSdk(cmd).getPtzScope(cmd);
    }
 
    public Boolean setZeroPtz(CameraCmd cmd) {
        return getSdk(cmd).setZeroPtz(cmd);
    }
 
    public Boolean set3DPosition(CameraCmd cmd) {
        return getSdk(cmd).set3DPosition(cmd);
    }
 
    public PtzParamDTO getGisInfo(CameraCmd cmd) {
        return getSdk(cmd).getGisInfo(cmd);
    }
 
    public Double correctPitch(CameraCmd cmd) {
        return getSdk(cmd).correctPitch(cmd);
    }
 
    // ========== 需要判断类型的操作 ==========
 
    public PtzDto getPtz(CameraCmd cmd) {
        return getSdk(cmd).getPtz(cmd);
    }
 
    @SdkOperate
    public Boolean setPtz(CameraCmd cmd) {
        return getSdk(cmd).setPtz(cmd);
    }
 
    // ========== 抓图(重载方法) ==========
 
    @SdkOperate
    public String picCutCate(CameraCmd cmd) {
        return getSdk(cmd).picCutCate(cmd);
    }
 
    @SdkOperate
    public String picCutCate(CameraCmd cmd, String bucketName, String objectName) {
        return getSdk(cmd).picCutCate(cmd, bucketName, objectName);
    }
 
    // ========== 录像 ==========
    public String record(CameraCmd cmd) {
        return getSdk(cmd).record(cmd);
    }
 
    public Boolean recordStart(CameraCmd cmd) {
        CameraSDK sdk = getSdk(cmd);
        Boolean result = sdk.recordStart(cmd);
        if (result != null && result) {
            RecordManager.addRecord(cmd.getCameraId() + "_" + cmd.getChanNo());
        }
        return result;
    }
 
    public String recordStop(CameraCmd cmd) {
        CameraSDK sdk = getSdk(cmd);
        String url = sdk.recordStop(cmd);
        String key = cmd.getCameraId() + "_" + cmd.getChanNo();
        RecordManager.stopRecord(key);
        return url;
    }
 
    // ========== 智能引导 ==========
 
    @SdkOperate
    public Boolean guideTargetPosition(CameraCmd cmd) {
        CameraSDK sdk = getSdk(cmd);
        ArdCamera camera = getCamera(cmd.getCameraId());
        String cameraId = camera.getId();
 
        // 判断是否超出引导距离
        if (isOutMaxDistance(cameraId, cmd.getTargetPosition())) {
            throw new CameraSDKException(CameraSDKException.ErrorCode.OVER_GUIDANCE_RANGE.getCode(),
                    cameraId, "目标超出最大引导距离");
        }
 
        // 判断是否在不可视域
        if (isInNoViewingZone(cameraId, cmd.getTargetPosition())) {
            throw new CameraSDKException(CameraSDKException.ErrorCode.BEYOND_VISIBILITY_RANGE.getCode(),
                    cameraId, "目标在不可视区域内");
        }
 
        cmd.setBaseYaw(camera.getBaseYaw());
        return sdk.guideTargetPosition(cmd);
    }
 
    // ========== 在线检测 ==========
 
    public boolean isOnLine(CameraCmd cmd) {
        Long loginId = redisService.getCacheMapValue(CacheConstants.CAMERA_ONLINE, cmd.getCameraId());
        if (loginId == null) {
            return false;
        }
        cmd.setLoginId(loginId);
        CameraSDK sdk = getSdk(cmd);
        return sdk.isOnLine(cmd);
    }
 
    // ========== 通道信息 ==========
 
    public List<ArdChannel> getChannels(ArdCamera camera) {
        Long loginId = redisService.getCacheMapValue(CacheConstants.CAMERA_ONLINE, camera.getId());
        if (loginId == null) {
            return new ArrayList<>();
        }
        CameraSDK sdk = cameraSDKFactory.createCameraSDK(camera.getFactory());
        return sdk.getChannels(camera);
    }
 
    // ========== 视频标签 ==========
 
    public AjaxResult getCameraVideoLable(CameraCmd cmd) {
        CameraSDK sdk = cameraSDKFactory.createCameraSDK("1"); // 硬编码问题建议修复
        try {
            List<Map<String, Object>> result = sdk.getCameraVideoLable(SecurityUtils.getUserId(), cmd);
            return AjaxResult.success(result);
        } catch (ServiceException e) {
            return AjaxResult.error(e.getMessage());
        }
    }
 
    // ========== 定时任务 ==========
 
    @Scheduled(fixedRate = 30 * 1000)
    public void checkVideoTimeout() {
        Date now = new Date();
        RecordManager.getActiveRecords().forEach((name, date) -> {
            long duration = now.getTime() - date.getTime();
            if (duration > 15 * 60 * 1000) {
                CameraCmd cmd = new CameraCmd();
                String cameraId = name.split("_")[0];
                Integer chanNo = Integer.parseInt(name.split("_")[1]);
                cmd.setCameraId(cameraId);
                cmd.setChanNo(chanNo);
                recordStop(cmd);
                log.info("录像超时检测:停止{}_{}的录像,已录像时间超过15分钟", cameraId, chanNo);
            }
        });
    }
 
    // ========== 私有辅助方法 ==========
 
    /**
     * 获取SDK实例(会校验参数、相机存在性、在线状态)
     */
    private CameraSDK getSdk(CameraCmd cmd) {
        if (cmd == null || cmd.getCameraId() == null || cmd.getCameraId().isBlank()) {
            throw CameraSDKException.cameraNotExist(null);
        }
 
        Long loginId = redisService.getCacheMapValue(CacheConstants.CAMERA_ONLINE, cmd.getCameraId());
        if (loginId == null) {
            throw CameraSDKException.cameraOffline(cmd.getCameraId());
        }
        cmd.setLoginId(loginId);
 
        ArdCamera camera = getCamera(cmd.getCameraId());
        return cameraSDKFactory.createCameraSDK(camera.getFactory());
    }
 
    /**
     * 获取相机信息(会校验存在性)
     */
    private ArdCamera getCamera(String cameraId) {
        ArdCamera camera = redisService.getCacheMapValue(CacheConstants.CAMERA_LIST, cameraId);
        if (camera == null) {
            throw CameraSDKException.cameraNotExist(cameraId);
        }
        return camera;
    }
 
    // 判断坐标是否超出引导范围
    private Boolean isOutMaxDistance(String cameraId, Point targetPoint) {
        ArdCamera camera = getCamera(cameraId);
        Point cameraCoordinate = new Point(camera.getLongitude(), camera.getLatitude(), camera.getAltitude());
        double distance = GisUtil.getDistance(cameraCoordinate, targetPoint);
 
        if (camera.getCamMaxVisibleDistance() == null) {
            throw new CameraSDKException(CameraSDKException.ErrorCode.CAMERA_MAX_VISIBLE_DISTANCE_NULL.getCode(),
                    cameraId, CameraSDKException.ErrorCode.CAMERA_MAX_VISIBLE_DISTANCE_NULL.getMsg());
        }
        return distance > camera.getCamMaxVisibleDistance();
    }
 
    // 判断坐标是否在不可视域
    private Boolean isInNoViewingZone(String cameraId, Point targetPoint) {
        ArdNoViewingZone query = new ArdNoViewingZone();
        query.setCameraId(cameraId);
        List<ArdNoViewingZone> zones = ardNoViewingZoneService.selectArdNoViewingZoneList(query);
        if (zones.isEmpty()) {
            return false;
        }
 
        List<Point> points = new ArrayList<>();
        for (ArdNoViewingZone zone : zones) {
            String[] parts = zone.getPoi().split(",");
            for (int i = 0; i < parts.length; i += 3) {
                Point point = new Point();
                point.setLongitude(Double.valueOf(parts[i]));
                point.setLatitude(Double.valueOf(parts[i + 1]));
                points.add(point);
            }
        }
        return GisUtil.isInPolygon(targetPoint, points);
    }
}