Commit 7083f30fc4ffd7af893e612c9c8cb1bf612faa6c
1 parent
ca1719da
康宝
Showing
5 changed files
with
580 additions
and
551 deletions
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
| ... | ... | @@ -26,7 +26,9 @@ import io.swagger.v3.oas.annotations.Operation; |
| 26 | 26 | import io.swagger.v3.oas.annotations.Parameter; |
| 27 | 27 | import io.swagger.v3.oas.annotations.security.SecurityRequirement; |
| 28 | 28 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 29 | +import org.apache.commons.collections4.CollectionUtils; | |
| 29 | 30 | import org.apache.commons.compress.utils.IOUtils; |
| 31 | +import org.apache.commons.lang3.StringUtils; | |
| 30 | 32 | import org.apache.ibatis.annotations.Options; |
| 31 | 33 | import org.slf4j.Logger; |
| 32 | 34 | import org.slf4j.LoggerFactory; |
| ... | ... | @@ -48,539 +50,568 @@ import java.io.InputStream; |
| 48 | 50 | import java.nio.file.Files; |
| 49 | 51 | import java.text.ParseException; |
| 50 | 52 | import java.util.*; |
| 53 | +import java.util.stream.Collectors; | |
| 51 | 54 | |
| 52 | -@Tag(name = "国标设备查询", description = "国标设备查询") | |
| 55 | +@Tag(name = "国标设备查询", description = "国标设备查询") | |
| 53 | 56 | @SuppressWarnings("rawtypes") |
| 54 | 57 | |
| 55 | 58 | @RestController |
| 56 | 59 | @RequestMapping("/api/device/query") |
| 57 | 60 | public class DeviceQuery { |
| 58 | - | |
| 59 | - private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); | |
| 60 | - | |
| 61 | - @Autowired | |
| 62 | - private IVideoManagerStorage storager; | |
| 63 | - | |
| 64 | - @Autowired | |
| 65 | - private IDeviceChannelService deviceChannelService; | |
| 66 | - | |
| 67 | - @Autowired | |
| 68 | - private IRedisCatchStorage redisCatchStorage; | |
| 69 | - | |
| 70 | - @Autowired | |
| 71 | - private IInviteStreamService inviteStreamService; | |
| 72 | - | |
| 73 | - @Autowired | |
| 74 | - private SIPCommander cmder; | |
| 75 | - | |
| 76 | - @Autowired | |
| 77 | - private DeferredResultHolder resultHolder; | |
| 78 | - | |
| 79 | - @Autowired | |
| 80 | - private IDeviceService deviceService; | |
| 81 | - | |
| 82 | - @Autowired | |
| 83 | - private DynamicTask dynamicTask; | |
| 84 | - | |
| 85 | - /** | |
| 86 | - * 使用ID查询国标设备 | |
| 87 | - * @param deviceId 国标ID | |
| 88 | - * @return 国标设备 | |
| 89 | - */ | |
| 90 | - @Operation(summary = "查询国标设备", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 91 | - @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 92 | - @GetMapping("/devices/{deviceId}") | |
| 93 | - public Device devices(@PathVariable String deviceId){ | |
| 94 | - | |
| 95 | - return storager.queryVideoDevice(deviceId); | |
| 96 | - } | |
| 97 | - | |
| 98 | - /** | |
| 99 | - * 分页查询国标设备 | |
| 100 | - * @param page 当前页 | |
| 101 | - * @param count 每页查询数量 | |
| 102 | - * @return 分页国标列表 | |
| 103 | - */ | |
| 104 | - @Operation(summary = "分页查询国标设备", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 105 | - @Parameter(name = "page", description = "当前页", required = true) | |
| 106 | - @Parameter(name = "count", description = "每页查询数量", required = true) | |
| 107 | - @GetMapping("/devices") | |
| 108 | - @Options() | |
| 109 | - public PageInfo<Device> devices(int page, int count){ | |
| 61 | + | |
| 62 | + private final static Logger logger = LoggerFactory.getLogger(DeviceQuery.class); | |
| 63 | + | |
| 64 | + @Autowired | |
| 65 | + private IVideoManagerStorage storager; | |
| 66 | + | |
| 67 | + @Autowired | |
| 68 | + private IDeviceChannelService deviceChannelService; | |
| 69 | + | |
| 70 | + @Autowired | |
| 71 | + private IRedisCatchStorage redisCatchStorage; | |
| 72 | + | |
| 73 | + @Autowired | |
| 74 | + private IInviteStreamService inviteStreamService; | |
| 75 | + | |
| 76 | + @Autowired | |
| 77 | + private SIPCommander cmder; | |
| 78 | + | |
| 79 | + @Autowired | |
| 80 | + private DeferredResultHolder resultHolder; | |
| 81 | + | |
| 82 | + @Autowired | |
| 83 | + private IDeviceService deviceService; | |
| 84 | + | |
| 85 | + @Autowired | |
| 86 | + private DynamicTask dynamicTask; | |
| 87 | + | |
| 88 | + /** | |
| 89 | + * 使用ID查询国标设备 | |
| 90 | + * | |
| 91 | + * @param deviceId 国标ID | |
| 92 | + * @return 国标设备 | |
| 93 | + */ | |
| 94 | + @Operation(summary = "查询国标设备", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 95 | + @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 96 | + @GetMapping("/devices/{deviceId}") | |
| 97 | + public Device devices(@PathVariable String deviceId) { | |
| 98 | + | |
| 99 | + return storager.queryVideoDevice(deviceId); | |
| 100 | + } | |
| 101 | + | |
| 102 | + /** | |
| 103 | + * 分页查询国标设备 | |
| 104 | + * | |
| 105 | + * @param page 当前页 | |
| 106 | + * @param count 每页查询数量 | |
| 107 | + * @return 分页国标列表 | |
| 108 | + */ | |
| 109 | + @Operation(summary = "分页查询国标设备", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 110 | + @Parameter(name = "page", description = "当前页", required = true) | |
| 111 | + @Parameter(name = "count", description = "每页查询数量", required = true) | |
| 112 | + @GetMapping("/devices") | |
| 113 | + @Options() | |
| 114 | + public PageInfo<Device> devices(int page, int count) { | |
| 110 | 115 | // if (page == null) page = 0; |
| 111 | 116 | // if (count == null) count = 20; |
| 112 | - return storager.queryVideoDeviceList(page, count,null); | |
| 113 | - } | |
| 114 | - | |
| 115 | - /** | |
| 116 | - * 分页查询通道数 | |
| 117 | - * | |
| 118 | - * @param deviceId 设备id | |
| 119 | - * @param page 当前页 | |
| 120 | - * @param count 每页条数 | |
| 121 | - * @param query 查询内容 | |
| 122 | - * @param online 是否在线 在线 true / 离线 false | |
| 123 | - * @param channelType 设备 false/子目录 true | |
| 124 | - * @param catalogUnderDevice 是否直属与设备的目录 | |
| 125 | - * @return 通道列表 | |
| 126 | - */ | |
| 127 | - @GetMapping("/devices/{deviceId}/channels") | |
| 128 | - @Operation(summary = "分页查询通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 129 | - @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 130 | - @Parameter(name = "page", description = "当前页", required = true) | |
| 131 | - @Parameter(name = "count", description = "每页查询数量", required = true) | |
| 132 | - @Parameter(name = "query", description = "查询内容") | |
| 133 | - @Parameter(name = "online", description = "是否在线") | |
| 134 | - @Parameter(name = "channelType", description = "设备/子目录-> false/true") | |
| 135 | - @Parameter(name = "catalogUnderDevice", description = "是否直属与设备的目录") | |
| 136 | - public PageInfo<DeviceChannel> channels(@PathVariable String deviceId, | |
| 137 | - int page, int count, | |
| 138 | - @RequestParam(required = false) String query, | |
| 139 | - @RequestParam(required = false) Boolean online, | |
| 140 | - @RequestParam(required = false) Boolean channelType, | |
| 141 | - @RequestParam(required = false) Boolean catalogUnderDevice) { | |
| 142 | - if (ObjectUtils.isEmpty(query)) { | |
| 143 | - query = null; | |
| 144 | - } | |
| 145 | - | |
| 146 | - return storager.queryChannelsByDeviceId(deviceId, query, channelType, online, catalogUnderDevice, page, count); | |
| 147 | - } | |
| 148 | - | |
| 149 | - /** | |
| 150 | - * 同步设备通道 | |
| 151 | - * @param deviceId 设备id | |
| 152 | - * @return | |
| 153 | - */ | |
| 154 | - @Operation(summary = "同步设备通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 155 | - @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 156 | - @GetMapping("/devices/{deviceId}/sync") | |
| 157 | - public WVPResult<SyncStatus> devicesSync(@PathVariable String deviceId){ | |
| 158 | - | |
| 159 | - if (logger.isDebugEnabled()) { | |
| 160 | - logger.debug("设备通道信息同步API调用,deviceId:" + deviceId); | |
| 161 | - } | |
| 162 | - Device device = storager.queryVideoDevice(deviceId); | |
| 163 | - boolean status = deviceService.isSyncRunning(deviceId); | |
| 164 | - // 已存在则返回进度 | |
| 165 | - if (status) { | |
| 166 | - SyncStatus channelSyncStatus = deviceService.getChannelSyncStatus(deviceId); | |
| 167 | - return WVPResult.success(channelSyncStatus); | |
| 168 | - } | |
| 169 | - deviceService.sync(device); | |
| 170 | - | |
| 171 | - WVPResult<SyncStatus> wvpResult = new WVPResult<>(); | |
| 172 | - wvpResult.setCode(0); | |
| 173 | - wvpResult.setMsg("开始同步"); | |
| 174 | - return wvpResult; | |
| 175 | - } | |
| 176 | - | |
| 177 | - /** | |
| 178 | - * 移除设备 | |
| 179 | - * @param deviceId 设备id | |
| 180 | - * @return | |
| 181 | - */ | |
| 182 | - @Operation(summary = "移除设备", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 183 | - @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 184 | - @DeleteMapping("/devices/{deviceId}/delete") | |
| 185 | - public String delete(@PathVariable String deviceId){ | |
| 186 | - | |
| 187 | - if (logger.isDebugEnabled()) { | |
| 188 | - logger.debug("设备信息删除API调用,deviceId:" + deviceId); | |
| 189 | - } | |
| 190 | - | |
| 191 | - // 清除redis记录 | |
| 192 | - boolean isSuccess = deviceService.delete(deviceId); | |
| 193 | - if (isSuccess) { | |
| 194 | - inviteStreamService.clearInviteInfo(deviceId); | |
| 195 | - // 停止此设备的订阅更新 | |
| 196 | - Set<String> allKeys = dynamicTask.getAllKeys(); | |
| 197 | - for (String key : allKeys) { | |
| 198 | - if (key.startsWith(deviceId)) { | |
| 199 | - Runnable runnable = dynamicTask.get(key); | |
| 200 | - if (runnable instanceof ISubscribeTask) { | |
| 201 | - ISubscribeTask subscribeTask = (ISubscribeTask) runnable; | |
| 202 | - subscribeTask.stop(null); | |
| 203 | - } | |
| 204 | - dynamicTask.stop(key); | |
| 205 | - } | |
| 206 | - } | |
| 207 | - JSONObject json = new JSONObject(); | |
| 208 | - json.put("deviceId", deviceId); | |
| 209 | - return json.toString(); | |
| 210 | - } else { | |
| 211 | - logger.warn("设备信息删除API调用失败!"); | |
| 212 | - throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备信息删除API调用失败!"); | |
| 213 | - } | |
| 214 | - } | |
| 215 | - | |
| 216 | - /** | |
| 217 | - * 分页查询子目录通道 | |
| 218 | - * @param deviceId 通道id | |
| 219 | - * @param channelId 通道id | |
| 220 | - * @param page 当前页 | |
| 221 | - * @param count 每页条数 | |
| 222 | - * @param query 查询内容 | |
| 223 | - * @param online 是否在线 | |
| 224 | - * @param channelType 通道类型 | |
| 225 | - * @return 子通道列表 | |
| 226 | - */ | |
| 227 | - @Operation(summary = "分页查询子目录通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 228 | - @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 229 | - @Parameter(name = "channelId", description = "通道国标编号", required = true) | |
| 230 | - @Parameter(name = "page", description = "当前页", required = true) | |
| 231 | - @Parameter(name = "count", description = "每页查询数量", required = true) | |
| 232 | - @Parameter(name = "query", description = "查询内容") | |
| 233 | - @Parameter(name = "online", description = "是否在线") | |
| 234 | - @Parameter(name = "channelType", description = "设备/子目录-> false/true") | |
| 235 | - @GetMapping("/sub_channels/{deviceId}/{channelId}/channels") | |
| 236 | - public PageInfo<DeviceChannel> subChannels(@PathVariable String deviceId, | |
| 237 | - @PathVariable String channelId, | |
| 238 | - int page, | |
| 239 | - int count, | |
| 240 | - @RequestParam(required = false) String query, | |
| 241 | - @RequestParam(required = false) Boolean online, | |
| 242 | - @RequestParam(required = false) Boolean channelType){ | |
| 243 | - | |
| 244 | - DeviceChannel deviceChannel = storager.queryChannel(deviceId,channelId); | |
| 245 | - if (deviceChannel == null) { | |
| 246 | - PageInfo<DeviceChannel> deviceChannelPageResult = new PageInfo<>(); | |
| 247 | - return deviceChannelPageResult; | |
| 248 | - } | |
| 249 | - | |
| 250 | - return storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count); | |
| 251 | - } | |
| 252 | - | |
| 253 | - /** | |
| 254 | - * 更新通道信息 | |
| 255 | - * @param deviceId 设备id | |
| 256 | - * @param channel 通道 | |
| 257 | - * @return | |
| 258 | - */ | |
| 259 | - @Operation(summary = "更新通道信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 260 | - @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 261 | - @Parameter(name = "channel", description = "通道信息", required = true) | |
| 262 | - @PostMapping("/channel/update/{deviceId}") | |
| 263 | - public void updateChannel(@PathVariable String deviceId,DeviceChannel channel){ | |
| 264 | - deviceChannelService.updateChannel(deviceId, channel); | |
| 265 | - } | |
| 266 | - | |
| 267 | - @Operation(summary = "修改通道的码流类型", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 268 | - @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 269 | - @Parameter(name = "channel", description = "通道信息", required = true) | |
| 270 | - @PostMapping("/channel/stream/identification/update/") | |
| 271 | - public void updateChannelStreamIdentification(DeviceChannel channel){ | |
| 272 | - deviceChannelService.updateChannelStreamIdentification(channel); | |
| 273 | - } | |
| 274 | - | |
| 275 | - /** | |
| 276 | - * 修改数据流传输模式 | |
| 277 | - * @param deviceId 设备id | |
| 278 | - * @param streamMode 数据流传输模式 | |
| 279 | - * @return | |
| 280 | - */ | |
| 281 | - @Operation(summary = "修改数据流传输模式", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 282 | - @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 283 | - @Parameter(name = "streamMode", description = "数据流传输模式, 取值:" + | |
| 284 | - "UDP(udp传输),TCP-ACTIVE(tcp主动模式,暂不支持),TCP-PASSIVE(tcp被动模式)", required = true) | |
| 285 | - @PostMapping("/transport/{deviceId}/{streamMode}") | |
| 286 | - public void updateTransport(@PathVariable String deviceId, @PathVariable String streamMode){ | |
| 287 | - Device device = deviceService.getDevice(deviceId); | |
| 288 | - device.setStreamMode(streamMode); | |
| 289 | - deviceService.updateCustomDevice(device); | |
| 290 | - } | |
| 291 | - | |
| 292 | - /** | |
| 293 | - * 添加设备信息 | |
| 294 | - * @param device 设备信息 | |
| 295 | - * @return | |
| 296 | - */ | |
| 297 | - @Operation(summary = "添加设备信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 298 | - @Parameter(name = "device", description = "设备", required = true) | |
| 299 | - @PostMapping("/device/add/") | |
| 300 | - public void addDevice(Device device){ | |
| 301 | - | |
| 302 | - if (device == null || device.getDeviceId() == null) { | |
| 303 | - throw new ControllerException(ErrorCode.ERROR400); | |
| 304 | - } | |
| 305 | - | |
| 306 | - // 查看deviceId是否存在 | |
| 307 | - boolean exist = deviceService.isExist(device.getDeviceId()); | |
| 308 | - if (exist) { | |
| 309 | - throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备编号已存在"); | |
| 310 | - } | |
| 311 | - deviceService.addDevice(device); | |
| 312 | - } | |
| 313 | - | |
| 314 | - /** | |
| 315 | - * 更新设备信息 | |
| 316 | - * @param device 设备信息 | |
| 317 | - * @return | |
| 318 | - */ | |
| 319 | - @Operation(summary = "更新设备信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 320 | - @Parameter(name = "device", description = "设备", required = true) | |
| 321 | - @PostMapping("/device/update/") | |
| 322 | - public void updateDevice(Device device){ | |
| 323 | - | |
| 324 | - if (device != null && device.getDeviceId() != null) { | |
| 325 | - deviceService.updateCustomDevice(device); | |
| 326 | - } | |
| 327 | - } | |
| 328 | - | |
| 329 | - /** | |
| 330 | - * 设备状态查询请求API接口 | |
| 331 | - * | |
| 332 | - * @param deviceId 设备id | |
| 333 | - */ | |
| 334 | - @Operation(summary = "设备状态查询", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 335 | - @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 336 | - @GetMapping("/devices/{deviceId}/status") | |
| 337 | - public DeferredResult<ResponseEntity<String>> deviceStatusApi(@PathVariable String deviceId) { | |
| 338 | - if (logger.isDebugEnabled()) { | |
| 339 | - logger.debug("设备状态查询API调用"); | |
| 340 | - } | |
| 341 | - Device device = storager.queryVideoDevice(deviceId); | |
| 342 | - String uuid = UUID.randomUUID().toString(); | |
| 343 | - String key = DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId; | |
| 344 | - DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2*1000L); | |
| 345 | - if(device == null) { | |
| 346 | - result.setResult(new ResponseEntity(String.format("设备%s不存在", deviceId),HttpStatus.OK)); | |
| 347 | - return result; | |
| 348 | - } | |
| 349 | - try { | |
| 350 | - cmder.deviceStatusQuery(device, event -> { | |
| 351 | - RequestMessage msg = new RequestMessage(); | |
| 352 | - msg.setId(uuid); | |
| 353 | - msg.setKey(key); | |
| 354 | - msg.setData(String.format("获取设备状态失败,错误码: %s, %s", event.statusCode, event.msg)); | |
| 355 | - resultHolder.invokeResult(msg); | |
| 356 | - }); | |
| 357 | - } catch (InvalidArgumentException | SipException | ParseException e) { | |
| 358 | - logger.error("[命令发送失败] 获取设备状态: {}", e.getMessage()); | |
| 359 | - throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); | |
| 360 | - } | |
| 361 | - result.onTimeout(()->{ | |
| 362 | - logger.warn(String.format("获取设备状态超时")); | |
| 363 | - // 释放rtpserver | |
| 364 | - RequestMessage msg = new RequestMessage(); | |
| 365 | - msg.setId(uuid); | |
| 366 | - msg.setKey(key); | |
| 367 | - msg.setData("Timeout. Device did not response to this command."); | |
| 368 | - resultHolder.invokeResult(msg); | |
| 369 | - }); | |
| 370 | - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId, uuid, result); | |
| 371 | - return result; | |
| 372 | - } | |
| 373 | - | |
| 374 | - /** | |
| 375 | - * 设备报警查询请求API接口 | |
| 376 | - * @param deviceId 设备id | |
| 377 | - * @param startPriority 报警起始级别(可选) | |
| 378 | - * @param endPriority 报警终止级别(可选) | |
| 379 | - * @param alarmMethod 报警方式条件(可选) | |
| 380 | - * @param alarmType 报警类型 | |
| 381 | - * @param startTime 报警发生起始时间(可选) | |
| 382 | - * @param endTime 报警发生终止时间(可选) | |
| 383 | - * @return true = 命令发送成功 | |
| 384 | - */ | |
| 385 | - @Operation(summary = "设备报警查询", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 386 | - @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 387 | - @Parameter(name = "startPriority", description = "报警起始级别") | |
| 388 | - @Parameter(name = "endPriority", description = "报警终止级别") | |
| 389 | - @Parameter(name = "alarmMethod", description = "报警方式条件") | |
| 390 | - @Parameter(name = "alarmType", description = "报警类型") | |
| 391 | - @Parameter(name = "startTime", description = "报警发生起始时间") | |
| 392 | - @Parameter(name = "endTime", description = "报警发生终止时间") | |
| 393 | - @GetMapping("/alarm/{deviceId}") | |
| 394 | - public DeferredResult<ResponseEntity<String>> alarmApi(@PathVariable String deviceId, | |
| 395 | - @RequestParam(required = false) String startPriority, | |
| 396 | - @RequestParam(required = false) String endPriority, | |
| 397 | - @RequestParam(required = false) String alarmMethod, | |
| 398 | - @RequestParam(required = false) String alarmType, | |
| 399 | - @RequestParam(required = false) String startTime, | |
| 400 | - @RequestParam(required = false) String endTime) { | |
| 401 | - if (logger.isDebugEnabled()) { | |
| 402 | - logger.debug("设备报警查询API调用"); | |
| 403 | - } | |
| 404 | - Device device = storager.queryVideoDevice(deviceId); | |
| 405 | - String key = DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId; | |
| 406 | - String uuid = UUID.randomUUID().toString(); | |
| 407 | - try { | |
| 408 | - cmder.alarmInfoQuery(device, startPriority, endPriority, alarmMethod, alarmType, startTime, endTime, event -> { | |
| 409 | - RequestMessage msg = new RequestMessage(); | |
| 410 | - msg.setId(uuid); | |
| 411 | - msg.setKey(key); | |
| 412 | - msg.setData(String.format("设备报警查询失败,错误码: %s, %s",event.statusCode, event.msg)); | |
| 413 | - resultHolder.invokeResult(msg); | |
| 414 | - }); | |
| 415 | - } catch (InvalidArgumentException | SipException | ParseException e) { | |
| 416 | - logger.error("[命令发送失败] 设备报警查询: {}", e.getMessage()); | |
| 417 | - throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); | |
| 418 | - } | |
| 419 | - DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String >> (3 * 1000L); | |
| 420 | - result.onTimeout(()->{ | |
| 421 | - logger.warn(String.format("设备报警查询超时")); | |
| 422 | - // 释放rtpserver | |
| 423 | - RequestMessage msg = new RequestMessage(); | |
| 424 | - msg.setId(uuid); | |
| 425 | - msg.setKey(key); | |
| 426 | - msg.setData("设备报警查询超时"); | |
| 427 | - resultHolder.invokeResult(msg); | |
| 428 | - }); | |
| 429 | - resultHolder.put(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId, uuid, result); | |
| 430 | - return result; | |
| 431 | - } | |
| 432 | - | |
| 433 | - | |
| 434 | - @GetMapping("/{deviceId}/sync_status") | |
| 435 | - @Operation(summary = "获取通道同步进度", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 436 | - @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 437 | - public WVPResult<SyncStatus> getSyncStatus(@PathVariable String deviceId) { | |
| 438 | - SyncStatus channelSyncStatus = deviceService.getChannelSyncStatus(deviceId); | |
| 439 | - WVPResult<SyncStatus> wvpResult = new WVPResult<>(); | |
| 440 | - if (channelSyncStatus == null) { | |
| 441 | - wvpResult.setCode(-1); | |
| 442 | - wvpResult.setMsg("同步尚未开始"); | |
| 443 | - }else { | |
| 444 | - wvpResult.setCode(ErrorCode.SUCCESS.getCode()); | |
| 445 | - wvpResult.setMsg(ErrorCode.SUCCESS.getMsg()); | |
| 446 | - wvpResult.setData(channelSyncStatus); | |
| 447 | - if (channelSyncStatus.getErrorMsg() != null) { | |
| 448 | - wvpResult.setMsg(channelSyncStatus.getErrorMsg()); | |
| 449 | - } | |
| 450 | - } | |
| 451 | - return wvpResult; | |
| 452 | - } | |
| 453 | - | |
| 454 | - @GetMapping("/{deviceId}/subscribe_info") | |
| 455 | - @Operation(summary = "获取设备的订阅状态", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 456 | - @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 457 | - public WVPResult<Map<String, Integer>> getSubscribeInfo(@PathVariable String deviceId) { | |
| 458 | - Set<String> allKeys = dynamicTask.getAllKeys(); | |
| 459 | - Map<String, Integer> dialogStateMap = new HashMap<>(); | |
| 460 | - for (String key : allKeys) { | |
| 461 | - if (key.startsWith(deviceId)) { | |
| 462 | - ISubscribeTask subscribeTask = (ISubscribeTask)dynamicTask.get(key); | |
| 463 | - if (subscribeTask instanceof CatalogSubscribeTask) { | |
| 464 | - dialogStateMap.put("catalog", 1); | |
| 465 | - }else if (subscribeTask instanceof MobilePositionSubscribeTask) { | |
| 466 | - dialogStateMap.put("mobilePosition", 1); | |
| 467 | - } | |
| 468 | - } | |
| 469 | - } | |
| 470 | - WVPResult<Map<String, Integer>> wvpResult = new WVPResult<>(); | |
| 471 | - wvpResult.setCode(0); | |
| 472 | - wvpResult.setData(dialogStateMap); | |
| 473 | - return wvpResult; | |
| 474 | - } | |
| 475 | - | |
| 476 | - @GetMapping("/snap/{deviceId}/{channelId}") | |
| 477 | - @Operation(summary = "请求截图") | |
| 478 | - @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 479 | - @Parameter(name = "channelId", description = "通道国标编号", required = true) | |
| 480 | - @Parameter(name = "mark", description = "标识", required = false) | |
| 481 | - public void getSnap(HttpServletResponse resp, @PathVariable String deviceId, @PathVariable String channelId, @RequestParam(required = false) String mark) { | |
| 482 | - | |
| 483 | - try { | |
| 484 | - final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + (mark == null? ".jpg": ("_" + mark + ".jpg"))).toPath()); | |
| 485 | - resp.setContentType(MediaType.IMAGE_PNG_VALUE); | |
| 486 | - ServletOutputStream outputStream = resp.getOutputStream(); | |
| 487 | - IOUtils.copy(in, resp.getOutputStream()); | |
| 488 | - in.close(); | |
| 489 | - outputStream.close(); | |
| 490 | - } catch (IOException e) { | |
| 491 | - resp.setStatus(HttpServletResponse.SC_NOT_FOUND); | |
| 492 | - } | |
| 493 | - } | |
| 494 | - | |
| 495 | - /** | |
| 496 | - * 查询国标树 | |
| 497 | - * @param deviceId 设备ID | |
| 498 | - * @param parentId 父ID | |
| 499 | - * @param page 当前页 | |
| 500 | - * @param count 每页条数 | |
| 501 | - * @return 国标设备 | |
| 502 | - */ | |
| 503 | - @Operation(summary = "查询国标树") | |
| 504 | - @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 505 | - @Parameter(name = "parentId", description = "父级国标编号") | |
| 506 | - @Parameter(name = "onlyCatalog", description = "只获取目录") | |
| 507 | - @Parameter(name = "page", description = "当前页", required = true) | |
| 508 | - @Parameter(name = "count", description = "每页条数", required = true) | |
| 509 | - @GetMapping("/tree/{deviceId}") | |
| 510 | - public ResponseEntity<PageInfo> getTree(@PathVariable String deviceId, | |
| 511 | - @RequestParam(required = false) String parentId, | |
| 512 | - @RequestParam(required = false) Boolean onlyCatalog, | |
| 513 | - int page, int count){ | |
| 514 | - | |
| 515 | - | |
| 516 | - if (page <= 0) { | |
| 517 | - page = 1; | |
| 518 | - } | |
| 519 | - if (onlyCatalog == null) { | |
| 520 | - onlyCatalog = false; | |
| 521 | - } | |
| 522 | - | |
| 523 | - List<BaseTree<DeviceChannel>> treeData = deviceService.queryVideoDeviceTree(deviceId, parentId, onlyCatalog); | |
| 524 | - if (treeData == null || (page - 1) * count > treeData.size()) { | |
| 525 | - PageInfo<BaseTree<DeviceChannel>> pageInfo = new PageInfo<>(); | |
| 526 | - pageInfo.setPageNum(page); | |
| 527 | - pageInfo.setTotal(treeData == null? 0 : treeData.size()); | |
| 528 | - pageInfo.setSize(0); | |
| 529 | - pageInfo.setList(new ArrayList<>()); | |
| 530 | - return new ResponseEntity<>(pageInfo,HttpStatus.OK); | |
| 531 | - } | |
| 532 | - | |
| 533 | - int toIndex = Math.min(page * count, treeData.size()); | |
| 534 | - // 处理分页 | |
| 535 | - List<BaseTree<DeviceChannel>> trees = treeData.subList((page - 1) * count, toIndex); | |
| 536 | - PageInfo<BaseTree<DeviceChannel>> pageInfo = new PageInfo<>(); | |
| 537 | - pageInfo.setPageNum(page); | |
| 538 | - pageInfo.setTotal(treeData.size()); | |
| 539 | - pageInfo.setSize(trees.size()); | |
| 540 | - pageInfo.setList(trees); | |
| 541 | - | |
| 542 | - return new ResponseEntity<>(pageInfo,HttpStatus.OK); | |
| 543 | - } | |
| 544 | - | |
| 545 | - /** | |
| 546 | - * 查询国标树下的通道 | |
| 547 | - * @param deviceId 设备ID | |
| 548 | - * @param parentId 父ID | |
| 549 | - * @param page 当前页 | |
| 550 | - * @param count 每页条数 | |
| 551 | - * @return 国标设备 | |
| 552 | - */ | |
| 553 | - @Operation(summary = "查询国标树下的通道") | |
| 554 | - @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 555 | - @Parameter(name = "parentId", description = "父级国标编号") | |
| 556 | - @Parameter(name = "page", description = "当前页", required = true) | |
| 557 | - @Parameter(name = "count", description = "每页条数", required = true) | |
| 558 | - @GetMapping("/tree/channel/{deviceId}") | |
| 559 | - public ResponseEntity<PageInfo> getChannelInTreeNode(@PathVariable String deviceId, @RequestParam(required = false) String parentId, int page, int count){ | |
| 560 | - | |
| 561 | - if (page <= 0) { | |
| 562 | - page = 1; | |
| 563 | - } | |
| 564 | - | |
| 565 | - List<DeviceChannel> treeData = deviceService.queryVideoDeviceInTreeNode(deviceId, parentId); | |
| 566 | - if (treeData == null || (page - 1) * count > treeData.size()) { | |
| 567 | - PageInfo<BaseTree<DeviceChannel>> pageInfo = new PageInfo<>(); | |
| 568 | - pageInfo.setPageNum(page); | |
| 569 | - pageInfo.setTotal(treeData == null? 0 : treeData.size()); | |
| 570 | - pageInfo.setSize(0); | |
| 571 | - pageInfo.setList(new ArrayList<>()); | |
| 572 | - return new ResponseEntity<>(pageInfo,HttpStatus.OK); | |
| 573 | - } | |
| 574 | - | |
| 575 | - int toIndex = Math.min(page * count, treeData.size()); | |
| 576 | - // 处理分页 | |
| 577 | - List<DeviceChannel> trees = treeData.subList((page - 1) * count, toIndex); | |
| 578 | - PageInfo<DeviceChannel> pageInfo = new PageInfo<>(); | |
| 579 | - pageInfo.setPageNum(page); | |
| 580 | - pageInfo.setTotal(treeData.size()); | |
| 581 | - pageInfo.setSize(trees.size()); | |
| 582 | - pageInfo.setList(trees); | |
| 583 | - | |
| 584 | - return new ResponseEntity<>(pageInfo,HttpStatus.OK); | |
| 585 | - } | |
| 117 | + return storager.queryVideoDeviceList(page, count, null); | |
| 118 | + } | |
| 119 | + | |
| 120 | + /** | |
| 121 | + * 分页查询通道数 | |
| 122 | + * | |
| 123 | + * @param deviceId 设备id | |
| 124 | + * @param page 当前页 | |
| 125 | + * @param count 每页条数 | |
| 126 | + * @param query 查询内容 | |
| 127 | + * @param online 是否在线 在线 true / 离线 false | |
| 128 | + * @param channelType 设备 false/子目录 true | |
| 129 | + * @param catalogUnderDevice 是否直属与设备的目录 | |
| 130 | + * @return 通道列表 | |
| 131 | + */ | |
| 132 | + @GetMapping("/devices/{deviceId}/channels") | |
| 133 | + @Operation(summary = "分页查询通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 134 | + @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 135 | + @Parameter(name = "page", description = "当前页", required = true) | |
| 136 | + @Parameter(name = "count", description = "每页查询数量", required = true) | |
| 137 | + @Parameter(name = "query", description = "查询内容") | |
| 138 | + @Parameter(name = "online", description = "是否在线") | |
| 139 | + @Parameter(name = "channelType", description = "设备/子目录-> false/true") | |
| 140 | + @Parameter(name = "catalogUnderDevice", description = "是否直属与设备的目录") | |
| 141 | + public PageInfo<DeviceChannel> channels(@PathVariable String deviceId, | |
| 142 | + int page, int count, | |
| 143 | + @RequestParam(required = false) String query, | |
| 144 | + @RequestParam(required = false) String online, | |
| 145 | + @RequestParam(required = false) Boolean channelType, | |
| 146 | + @RequestParam(required = false) Boolean catalogUnderDevice) { | |
| 147 | + if (ObjectUtils.isEmpty(query)) { | |
| 148 | + query = null; | |
| 149 | + } | |
| 150 | + | |
| 151 | + Boolean onlineBoolean = null; | |
| 152 | + if (StringUtils.equalsAnyIgnoreCase("true", online)) { | |
| 153 | + onlineBoolean = true; | |
| 154 | + } else if (StringUtils.equalsAnyIgnoreCase("false", online)) { | |
| 155 | + onlineBoolean = false; | |
| 156 | + } else if (StringUtils.equalsAnyIgnoreCase("the", online)) { | |
| 157 | + page = 1; | |
| 158 | + count = 10000; | |
| 159 | + } | |
| 160 | + | |
| 161 | + PageInfo<DeviceChannel> pageInfo = storager.queryChannelsByDeviceId(deviceId, query, channelType, onlineBoolean, catalogUnderDevice, page, count); | |
| 162 | + if (StringUtils.equalsAnyIgnoreCase("the", online) && Objects.nonNull(pageInfo) && CollectionUtils.isNotEmpty(pageInfo.getList())) { | |
| 163 | + List<DeviceChannel> channels = pageInfo.getList().stream().filter(ch -> Objects.equals(ch.getChannelId(), "34020000001310000001") | |
| 164 | + || Objects.equals(ch.getChannelId(), "34020000001310000002")).collect(Collectors.toList()); | |
| 165 | + pageInfo.setList(channels); | |
| 166 | + } | |
| 167 | + return pageInfo; | |
| 168 | + } | |
| 169 | + | |
| 170 | + /** | |
| 171 | + * 同步设备通道 | |
| 172 | + * | |
| 173 | + * @param deviceId 设备id | |
| 174 | + * @return | |
| 175 | + */ | |
| 176 | + @Operation(summary = "同步设备通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 177 | + @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 178 | + @GetMapping("/devices/{deviceId}/sync") | |
| 179 | + public WVPResult<SyncStatus> devicesSync(@PathVariable String deviceId) { | |
| 180 | + | |
| 181 | + if (logger.isDebugEnabled()) { | |
| 182 | + logger.debug("设备通道信息同步API调用,deviceId:" + deviceId); | |
| 183 | + } | |
| 184 | + Device device = storager.queryVideoDevice(deviceId); | |
| 185 | + boolean status = deviceService.isSyncRunning(deviceId); | |
| 186 | + // 已存在则返回进度 | |
| 187 | + if (status) { | |
| 188 | + SyncStatus channelSyncStatus = deviceService.getChannelSyncStatus(deviceId); | |
| 189 | + return WVPResult.success(channelSyncStatus); | |
| 190 | + } | |
| 191 | + deviceService.sync(device); | |
| 192 | + | |
| 193 | + WVPResult<SyncStatus> wvpResult = new WVPResult<>(); | |
| 194 | + wvpResult.setCode(0); | |
| 195 | + wvpResult.setMsg("开始同步"); | |
| 196 | + return wvpResult; | |
| 197 | + } | |
| 198 | + | |
| 199 | + /** | |
| 200 | + * 移除设备 | |
| 201 | + * | |
| 202 | + * @param deviceId 设备id | |
| 203 | + * @return | |
| 204 | + */ | |
| 205 | + @Operation(summary = "移除设备", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 206 | + @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 207 | + @DeleteMapping("/devices/{deviceId}/delete") | |
| 208 | + public String delete(@PathVariable String deviceId) { | |
| 209 | + | |
| 210 | + if (logger.isDebugEnabled()) { | |
| 211 | + logger.debug("设备信息删除API调用,deviceId:" + deviceId); | |
| 212 | + } | |
| 213 | + | |
| 214 | + // 清除redis记录 | |
| 215 | + boolean isSuccess = deviceService.delete(deviceId); | |
| 216 | + if (isSuccess) { | |
| 217 | + inviteStreamService.clearInviteInfo(deviceId); | |
| 218 | + // 停止此设备的订阅更新 | |
| 219 | + Set<String> allKeys = dynamicTask.getAllKeys(); | |
| 220 | + for (String key : allKeys) { | |
| 221 | + if (key.startsWith(deviceId)) { | |
| 222 | + Runnable runnable = dynamicTask.get(key); | |
| 223 | + if (runnable instanceof ISubscribeTask) { | |
| 224 | + ISubscribeTask subscribeTask = (ISubscribeTask) runnable; | |
| 225 | + subscribeTask.stop(null); | |
| 226 | + } | |
| 227 | + dynamicTask.stop(key); | |
| 228 | + } | |
| 229 | + } | |
| 230 | + JSONObject json = new JSONObject(); | |
| 231 | + json.put("deviceId", deviceId); | |
| 232 | + return json.toString(); | |
| 233 | + } else { | |
| 234 | + logger.warn("设备信息删除API调用失败!"); | |
| 235 | + throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备信息删除API调用失败!"); | |
| 236 | + } | |
| 237 | + } | |
| 238 | + | |
| 239 | + /** | |
| 240 | + * 分页查询子目录通道 | |
| 241 | + * | |
| 242 | + * @param deviceId 通道id | |
| 243 | + * @param channelId 通道id | |
| 244 | + * @param page 当前页 | |
| 245 | + * @param count 每页条数 | |
| 246 | + * @param query 查询内容 | |
| 247 | + * @param online 是否在线 | |
| 248 | + * @param channelType 通道类型 | |
| 249 | + * @return 子通道列表 | |
| 250 | + */ | |
| 251 | + @Operation(summary = "分页查询子目录通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 252 | + @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 253 | + @Parameter(name = "channelId", description = "通道国标编号", required = true) | |
| 254 | + @Parameter(name = "page", description = "当前页", required = true) | |
| 255 | + @Parameter(name = "count", description = "每页查询数量", required = true) | |
| 256 | + @Parameter(name = "query", description = "查询内容") | |
| 257 | + @Parameter(name = "online", description = "是否在线") | |
| 258 | + @Parameter(name = "channelType", description = "设备/子目录-> false/true") | |
| 259 | + @GetMapping("/sub_channels/{deviceId}/{channelId}/channels") | |
| 260 | + public PageInfo<DeviceChannel> subChannels(@PathVariable String deviceId, | |
| 261 | + @PathVariable String channelId, | |
| 262 | + int page, | |
| 263 | + int count, | |
| 264 | + @RequestParam(required = false) String query, | |
| 265 | + @RequestParam(required = false) Boolean online, | |
| 266 | + @RequestParam(required = false) Boolean channelType) { | |
| 267 | + | |
| 268 | + DeviceChannel deviceChannel = storager.queryChannel(deviceId, channelId); | |
| 269 | + if (deviceChannel == null) { | |
| 270 | + PageInfo<DeviceChannel> deviceChannelPageResult = new PageInfo<>(); | |
| 271 | + return deviceChannelPageResult; | |
| 272 | + } | |
| 273 | + | |
| 274 | + return storager.querySubChannels(deviceId, channelId, query, channelType, online, page, count); | |
| 275 | + } | |
| 276 | + | |
| 277 | + /** | |
| 278 | + * 更新通道信息 | |
| 279 | + * | |
| 280 | + * @param deviceId 设备id | |
| 281 | + * @param channel 通道 | |
| 282 | + * @return | |
| 283 | + */ | |
| 284 | + @Operation(summary = "更新通道信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 285 | + @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 286 | + @Parameter(name = "channel", description = "通道信息", required = true) | |
| 287 | + @PostMapping("/channel/update/{deviceId}") | |
| 288 | + public void updateChannel(@PathVariable String deviceId, DeviceChannel channel) { | |
| 289 | + deviceChannelService.updateChannel(deviceId, channel); | |
| 290 | + } | |
| 291 | + | |
| 292 | + @Operation(summary = "修改通道的码流类型", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 293 | + @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 294 | + @Parameter(name = "channel", description = "通道信息", required = true) | |
| 295 | + @PostMapping("/channel/stream/identification/update/") | |
| 296 | + public void updateChannelStreamIdentification(DeviceChannel channel) { | |
| 297 | + deviceChannelService.updateChannelStreamIdentification(channel); | |
| 298 | + } | |
| 299 | + | |
| 300 | + /** | |
| 301 | + * 修改数据流传输模式 | |
| 302 | + * | |
| 303 | + * @param deviceId 设备id | |
| 304 | + * @param streamMode 数据流传输模式 | |
| 305 | + * @return | |
| 306 | + */ | |
| 307 | + @Operation(summary = "修改数据流传输模式", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 308 | + @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 309 | + @Parameter(name = "streamMode", description = "数据流传输模式, 取值:" + | |
| 310 | + "UDP(udp传输),TCP-ACTIVE(tcp主动模式,暂不支持),TCP-PASSIVE(tcp被动模式)", required = true) | |
| 311 | + @PostMapping("/transport/{deviceId}/{streamMode}") | |
| 312 | + public void updateTransport(@PathVariable String deviceId, @PathVariable String streamMode) { | |
| 313 | + Device device = deviceService.getDevice(deviceId); | |
| 314 | + device.setStreamMode(streamMode); | |
| 315 | + deviceService.updateCustomDevice(device); | |
| 316 | + } | |
| 317 | + | |
| 318 | + /** | |
| 319 | + * 添加设备信息 | |
| 320 | + * | |
| 321 | + * @param device 设备信息 | |
| 322 | + * @return | |
| 323 | + */ | |
| 324 | + @Operation(summary = "添加设备信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 325 | + @Parameter(name = "device", description = "设备", required = true) | |
| 326 | + @PostMapping("/device/add/") | |
| 327 | + public void addDevice(Device device) { | |
| 328 | + | |
| 329 | + if (device == null || device.getDeviceId() == null) { | |
| 330 | + throw new ControllerException(ErrorCode.ERROR400); | |
| 331 | + } | |
| 332 | + | |
| 333 | + // 查看deviceId是否存在 | |
| 334 | + boolean exist = deviceService.isExist(device.getDeviceId()); | |
| 335 | + if (exist) { | |
| 336 | + throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备编号已存在"); | |
| 337 | + } | |
| 338 | + deviceService.addDevice(device); | |
| 339 | + } | |
| 340 | + | |
| 341 | + /** | |
| 342 | + * 更新设备信息 | |
| 343 | + * | |
| 344 | + * @param device 设备信息 | |
| 345 | + * @return | |
| 346 | + */ | |
| 347 | + @Operation(summary = "更新设备信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 348 | + @Parameter(name = "device", description = "设备", required = true) | |
| 349 | + @PostMapping("/device/update/") | |
| 350 | + public void updateDevice(Device device) { | |
| 351 | + | |
| 352 | + if (device != null && device.getDeviceId() != null) { | |
| 353 | + deviceService.updateCustomDevice(device); | |
| 354 | + } | |
| 355 | + } | |
| 356 | + | |
| 357 | + /** | |
| 358 | + * 设备状态查询请求API接口 | |
| 359 | + * | |
| 360 | + * @param deviceId 设备id | |
| 361 | + */ | |
| 362 | + @Operation(summary = "设备状态查询", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 363 | + @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 364 | + @GetMapping("/devices/{deviceId}/status") | |
| 365 | + public DeferredResult<ResponseEntity<String>> deviceStatusApi(@PathVariable String deviceId) { | |
| 366 | + if (logger.isDebugEnabled()) { | |
| 367 | + logger.debug("设备状态查询API调用"); | |
| 368 | + } | |
| 369 | + Device device = storager.queryVideoDevice(deviceId); | |
| 370 | + String uuid = UUID.randomUUID().toString(); | |
| 371 | + String key = DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId; | |
| 372 | + DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(2 * 1000L); | |
| 373 | + if (device == null) { | |
| 374 | + result.setResult(new ResponseEntity(String.format("设备%s不存在", deviceId), HttpStatus.OK)); | |
| 375 | + return result; | |
| 376 | + } | |
| 377 | + try { | |
| 378 | + cmder.deviceStatusQuery(device, event -> { | |
| 379 | + RequestMessage msg = new RequestMessage(); | |
| 380 | + msg.setId(uuid); | |
| 381 | + msg.setKey(key); | |
| 382 | + msg.setData(String.format("获取设备状态失败,错误码: %s, %s", event.statusCode, event.msg)); | |
| 383 | + resultHolder.invokeResult(msg); | |
| 384 | + }); | |
| 385 | + } catch (InvalidArgumentException | SipException | ParseException e) { | |
| 386 | + logger.error("[命令发送失败] 获取设备状态: {}", e.getMessage()); | |
| 387 | + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); | |
| 388 | + } | |
| 389 | + result.onTimeout(() -> { | |
| 390 | + logger.warn(String.format("获取设备状态超时")); | |
| 391 | + // 释放rtpserver | |
| 392 | + RequestMessage msg = new RequestMessage(); | |
| 393 | + msg.setId(uuid); | |
| 394 | + msg.setKey(key); | |
| 395 | + msg.setData("Timeout. Device did not response to this command."); | |
| 396 | + resultHolder.invokeResult(msg); | |
| 397 | + }); | |
| 398 | + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + deviceId, uuid, result); | |
| 399 | + return result; | |
| 400 | + } | |
| 401 | + | |
| 402 | + /** | |
| 403 | + * 设备报警查询请求API接口 | |
| 404 | + * | |
| 405 | + * @param deviceId 设备id | |
| 406 | + * @param startPriority 报警起始级别(可选) | |
| 407 | + * @param endPriority 报警终止级别(可选) | |
| 408 | + * @param alarmMethod 报警方式条件(可选) | |
| 409 | + * @param alarmType 报警类型 | |
| 410 | + * @param startTime 报警发生起始时间(可选) | |
| 411 | + * @param endTime 报警发生终止时间(可选) | |
| 412 | + * @return true = 命令发送成功 | |
| 413 | + */ | |
| 414 | + @Operation(summary = "设备报警查询", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 415 | + @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 416 | + @Parameter(name = "startPriority", description = "报警起始级别") | |
| 417 | + @Parameter(name = "endPriority", description = "报警终止级别") | |
| 418 | + @Parameter(name = "alarmMethod", description = "报警方式条件") | |
| 419 | + @Parameter(name = "alarmType", description = "报警类型") | |
| 420 | + @Parameter(name = "startTime", description = "报警发生起始时间") | |
| 421 | + @Parameter(name = "endTime", description = "报警发生终止时间") | |
| 422 | + @GetMapping("/alarm/{deviceId}") | |
| 423 | + public DeferredResult<ResponseEntity<String>> alarmApi(@PathVariable String deviceId, | |
| 424 | + @RequestParam(required = false) String startPriority, | |
| 425 | + @RequestParam(required = false) String endPriority, | |
| 426 | + @RequestParam(required = false) String alarmMethod, | |
| 427 | + @RequestParam(required = false) String alarmType, | |
| 428 | + @RequestParam(required = false) String startTime, | |
| 429 | + @RequestParam(required = false) String endTime) { | |
| 430 | + if (logger.isDebugEnabled()) { | |
| 431 | + logger.debug("设备报警查询API调用"); | |
| 432 | + } | |
| 433 | + Device device = storager.queryVideoDevice(deviceId); | |
| 434 | + String key = DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId; | |
| 435 | + String uuid = UUID.randomUUID().toString(); | |
| 436 | + try { | |
| 437 | + cmder.alarmInfoQuery(device, startPriority, endPriority, alarmMethod, alarmType, startTime, endTime, event -> { | |
| 438 | + RequestMessage msg = new RequestMessage(); | |
| 439 | + msg.setId(uuid); | |
| 440 | + msg.setKey(key); | |
| 441 | + msg.setData(String.format("设备报警查询失败,错误码: %s, %s", event.statusCode, event.msg)); | |
| 442 | + resultHolder.invokeResult(msg); | |
| 443 | + }); | |
| 444 | + } catch (InvalidArgumentException | SipException | ParseException e) { | |
| 445 | + logger.error("[命令发送失败] 设备报警查询: {}", e.getMessage()); | |
| 446 | + throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); | |
| 447 | + } | |
| 448 | + DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L); | |
| 449 | + result.onTimeout(() -> { | |
| 450 | + logger.warn(String.format("设备报警查询超时")); | |
| 451 | + // 释放rtpserver | |
| 452 | + RequestMessage msg = new RequestMessage(); | |
| 453 | + msg.setId(uuid); | |
| 454 | + msg.setKey(key); | |
| 455 | + msg.setData("设备报警查询超时"); | |
| 456 | + resultHolder.invokeResult(msg); | |
| 457 | + }); | |
| 458 | + resultHolder.put(DeferredResultHolder.CALLBACK_CMD_ALARM + deviceId, uuid, result); | |
| 459 | + return result; | |
| 460 | + } | |
| 461 | + | |
| 462 | + | |
| 463 | + @GetMapping("/{deviceId}/sync_status") | |
| 464 | + @Operation(summary = "获取通道同步进度", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 465 | + @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 466 | + public WVPResult<SyncStatus> getSyncStatus(@PathVariable String deviceId) { | |
| 467 | + SyncStatus channelSyncStatus = deviceService.getChannelSyncStatus(deviceId); | |
| 468 | + WVPResult<SyncStatus> wvpResult = new WVPResult<>(); | |
| 469 | + if (channelSyncStatus == null) { | |
| 470 | + wvpResult.setCode(-1); | |
| 471 | + wvpResult.setMsg("同步尚未开始"); | |
| 472 | + } else { | |
| 473 | + wvpResult.setCode(ErrorCode.SUCCESS.getCode()); | |
| 474 | + wvpResult.setMsg(ErrorCode.SUCCESS.getMsg()); | |
| 475 | + wvpResult.setData(channelSyncStatus); | |
| 476 | + if (channelSyncStatus.getErrorMsg() != null) { | |
| 477 | + wvpResult.setMsg(channelSyncStatus.getErrorMsg()); | |
| 478 | + } | |
| 479 | + } | |
| 480 | + return wvpResult; | |
| 481 | + } | |
| 482 | + | |
| 483 | + @GetMapping("/{deviceId}/subscribe_info") | |
| 484 | + @Operation(summary = "获取设备的订阅状态", security = @SecurityRequirement(name = JwtUtils.HEADER)) | |
| 485 | + @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 486 | + public WVPResult<Map<String, Integer>> getSubscribeInfo(@PathVariable String deviceId) { | |
| 487 | + Set<String> allKeys = dynamicTask.getAllKeys(); | |
| 488 | + Map<String, Integer> dialogStateMap = new HashMap<>(); | |
| 489 | + for (String key : allKeys) { | |
| 490 | + if (key.startsWith(deviceId)) { | |
| 491 | + ISubscribeTask subscribeTask = (ISubscribeTask) dynamicTask.get(key); | |
| 492 | + if (subscribeTask instanceof CatalogSubscribeTask) { | |
| 493 | + dialogStateMap.put("catalog", 1); | |
| 494 | + } else if (subscribeTask instanceof MobilePositionSubscribeTask) { | |
| 495 | + dialogStateMap.put("mobilePosition", 1); | |
| 496 | + } | |
| 497 | + } | |
| 498 | + } | |
| 499 | + WVPResult<Map<String, Integer>> wvpResult = new WVPResult<>(); | |
| 500 | + wvpResult.setCode(0); | |
| 501 | + wvpResult.setData(dialogStateMap); | |
| 502 | + return wvpResult; | |
| 503 | + } | |
| 504 | + | |
| 505 | + @GetMapping("/snap/{deviceId}/{channelId}") | |
| 506 | + @Operation(summary = "请求截图") | |
| 507 | + @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 508 | + @Parameter(name = "channelId", description = "通道国标编号", required = true) | |
| 509 | + @Parameter(name = "mark", description = "标识", required = false) | |
| 510 | + public void getSnap(HttpServletResponse resp, @PathVariable String deviceId, @PathVariable String channelId, @RequestParam(required = false) String mark) { | |
| 511 | + | |
| 512 | + try { | |
| 513 | + final InputStream in = Files.newInputStream(new File("snap" + File.separator + deviceId + "_" + channelId + (mark == null ? ".jpg" : ("_" + mark + ".jpg"))).toPath()); | |
| 514 | + resp.setContentType(MediaType.IMAGE_PNG_VALUE); | |
| 515 | + ServletOutputStream outputStream = resp.getOutputStream(); | |
| 516 | + IOUtils.copy(in, resp.getOutputStream()); | |
| 517 | + in.close(); | |
| 518 | + outputStream.close(); | |
| 519 | + } catch (IOException e) { | |
| 520 | + resp.setStatus(HttpServletResponse.SC_NOT_FOUND); | |
| 521 | + } | |
| 522 | + } | |
| 523 | + | |
| 524 | + /** | |
| 525 | + * 查询国标树 | |
| 526 | + * | |
| 527 | + * @param deviceId 设备ID | |
| 528 | + * @param parentId 父ID | |
| 529 | + * @param page 当前页 | |
| 530 | + * @param count 每页条数 | |
| 531 | + * @return 国标设备 | |
| 532 | + */ | |
| 533 | + @Operation(summary = "查询国标树") | |
| 534 | + @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 535 | + @Parameter(name = "parentId", description = "父级国标编号") | |
| 536 | + @Parameter(name = "onlyCatalog", description = "只获取目录") | |
| 537 | + @Parameter(name = "page", description = "当前页", required = true) | |
| 538 | + @Parameter(name = "count", description = "每页条数", required = true) | |
| 539 | + @GetMapping("/tree/{deviceId}") | |
| 540 | + public ResponseEntity<PageInfo> getTree(@PathVariable String deviceId, | |
| 541 | + @RequestParam(required = false) String parentId, | |
| 542 | + @RequestParam(required = false) Boolean onlyCatalog, | |
| 543 | + int page, int count) { | |
| 544 | + | |
| 545 | + | |
| 546 | + if (page <= 0) { | |
| 547 | + page = 1; | |
| 548 | + } | |
| 549 | + if (onlyCatalog == null) { | |
| 550 | + onlyCatalog = false; | |
| 551 | + } | |
| 552 | + | |
| 553 | + List<BaseTree<DeviceChannel>> treeData = deviceService.queryVideoDeviceTree(deviceId, parentId, onlyCatalog); | |
| 554 | + if (treeData == null || (page - 1) * count > treeData.size()) { | |
| 555 | + PageInfo<BaseTree<DeviceChannel>> pageInfo = new PageInfo<>(); | |
| 556 | + pageInfo.setPageNum(page); | |
| 557 | + pageInfo.setTotal(treeData == null ? 0 : treeData.size()); | |
| 558 | + pageInfo.setSize(0); | |
| 559 | + pageInfo.setList(new ArrayList<>()); | |
| 560 | + return new ResponseEntity<>(pageInfo, HttpStatus.OK); | |
| 561 | + } | |
| 562 | + | |
| 563 | + int toIndex = Math.min(page * count, treeData.size()); | |
| 564 | + // 处理分页 | |
| 565 | + List<BaseTree<DeviceChannel>> trees = treeData.subList((page - 1) * count, toIndex); | |
| 566 | + PageInfo<BaseTree<DeviceChannel>> pageInfo = new PageInfo<>(); | |
| 567 | + pageInfo.setPageNum(page); | |
| 568 | + pageInfo.setTotal(treeData.size()); | |
| 569 | + pageInfo.setSize(trees.size()); | |
| 570 | + pageInfo.setList(trees); | |
| 571 | + | |
| 572 | + return new ResponseEntity<>(pageInfo, HttpStatus.OK); | |
| 573 | + } | |
| 574 | + | |
| 575 | + /** | |
| 576 | + * 查询国标树下的通道 | |
| 577 | + * | |
| 578 | + * @param deviceId 设备ID | |
| 579 | + * @param parentId 父ID | |
| 580 | + * @param page 当前页 | |
| 581 | + * @param count 每页条数 | |
| 582 | + * @return 国标设备 | |
| 583 | + */ | |
| 584 | + @Operation(summary = "查询国标树下的通道") | |
| 585 | + @Parameter(name = "deviceId", description = "设备国标编号", required = true) | |
| 586 | + @Parameter(name = "parentId", description = "父级国标编号") | |
| 587 | + @Parameter(name = "page", description = "当前页", required = true) | |
| 588 | + @Parameter(name = "count", description = "每页条数", required = true) | |
| 589 | + @GetMapping("/tree/channel/{deviceId}") | |
| 590 | + public ResponseEntity<PageInfo> getChannelInTreeNode(@PathVariable String deviceId, @RequestParam(required = false) String parentId, int page, int count) { | |
| 591 | + | |
| 592 | + if (page <= 0) { | |
| 593 | + page = 1; | |
| 594 | + } | |
| 595 | + | |
| 596 | + List<DeviceChannel> treeData = deviceService.queryVideoDeviceInTreeNode(deviceId, parentId); | |
| 597 | + if (treeData == null || (page - 1) * count > treeData.size()) { | |
| 598 | + PageInfo<BaseTree<DeviceChannel>> pageInfo = new PageInfo<>(); | |
| 599 | + pageInfo.setPageNum(page); | |
| 600 | + pageInfo.setTotal(treeData == null ? 0 : treeData.size()); | |
| 601 | + pageInfo.setSize(0); | |
| 602 | + pageInfo.setList(new ArrayList<>()); | |
| 603 | + return new ResponseEntity<>(pageInfo, HttpStatus.OK); | |
| 604 | + } | |
| 605 | + | |
| 606 | + int toIndex = Math.min(page * count, treeData.size()); | |
| 607 | + // 处理分页 | |
| 608 | + List<DeviceChannel> trees = treeData.subList((page - 1) * count, toIndex); | |
| 609 | + PageInfo<DeviceChannel> pageInfo = new PageInfo<>(); | |
| 610 | + pageInfo.setPageNum(page); | |
| 611 | + pageInfo.setTotal(treeData.size()); | |
| 612 | + pageInfo.setSize(trees.size()); | |
| 613 | + pageInfo.setList(trees); | |
| 614 | + | |
| 615 | + return new ResponseEntity<>(pageInfo, HttpStatus.OK); | |
| 616 | + } | |
| 586 | 617 | } | ... | ... |
src/main/resources/application-local.yml
| ... | ... | @@ -17,9 +17,9 @@ spring: |
| 17 | 17 | # [必须修改] 端口号 |
| 18 | 18 | port: 6379 |
| 19 | 19 | # [可选] 数据库 DB |
| 20 | - database: 7 | |
| 20 | + database: 8 | |
| 21 | 21 | # [可选] 访问密码,若你的redis服务器没有设置密码,就不需要用密码去连接 |
| 22 | -# password: guzijian | |
| 22 | + # password: guzijian | |
| 23 | 23 | # [可选] 超时时间 |
| 24 | 24 | timeout: 10000 |
| 25 | 25 | # mysql数据源 |
| ... | ... | @@ -30,7 +30,7 @@ spring: |
| 30 | 30 | master: |
| 31 | 31 | type: com.zaxxer.hikari.HikariDataSource |
| 32 | 32 | driver-class-name: com.mysql.cj.jdbc.Driver |
| 33 | - url: jdbc:mysql://127.0.0.1:3306/latest_wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true | |
| 33 | + url: jdbc:mysql://192.168.169.100:3306/wvp2?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true | |
| 34 | 34 | username: root |
| 35 | 35 | password: guzijian |
| 36 | 36 | hikari: |
| ... | ... | @@ -60,9 +60,10 @@ sip: |
| 60 | 60 | # 如果要监听多张网卡,可以使用逗号分隔多个IP, 例如: 192.168.1.4,10.0.0.4 |
| 61 | 61 | # 如果不明白,就使用0.0.0.0,大部分情况都是可以的 |
| 62 | 62 | # 请不要使用127.0.0.1,任何包括localhost在内的域名都是不可以的。 |
| 63 | - ip: 192.169.1.31 | |
| 63 | + ip: 0.0.0.0 | |
| 64 | 64 | # [可选] 28181服务监听的端口 |
| 65 | 65 | port: 5060 |
| 66 | + | |
| 66 | 67 | # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) |
| 67 | 68 | # 后两位为行业编码,定义参照附录D.3 |
| 68 | 69 | # 3701020049标识山东济南历下区 信息行业接入 |
| ... | ... | @@ -77,29 +78,29 @@ sip: |
| 77 | 78 | |
| 78 | 79 | #zlm 默认服务器配置 |
| 79 | 80 | media: |
| 80 | - id: cQ5byeSBKP2INt6l | |
| 81 | + id: guzijian1 | |
| 81 | 82 | # [必须修改] zlm服务器的内网IP |
| 82 | - ip: 192.169.1.31 | |
| 83 | + ip: 192.168.169.100 | |
| 83 | 84 | # [必须修改] zlm服务器的http.port |
| 84 | - http-port: 80 | |
| 85 | + http-port: 1091 | |
| 85 | 86 | # [可选] 返回流地址时的ip,置空使用 media.ip |
| 86 | - stream-ip: 192.169.1.31 | |
| 87 | + stream-ip: 192.168.169.100 | |
| 87 | 88 | # [可选] wvp在国标信令中使用的ip,此ip为摄像机可以访问到的ip, 置空使用 media.ip |
| 88 | - sdp-ip: 192.169.1.31 | |
| 89 | + sdp-ip: 192.168.169.100 | |
| 89 | 90 | # [可选] zlm服务器的hook所使用的IP, 默认使用sip.ip |
| 90 | - hook-ip: 192.169.1.31 | |
| 91 | + hook-ip: 192.168.169.100 | |
| 91 | 92 | # [可选] zlm服务器的http.sslport, 置空使用zlm配置文件配置 |
| 92 | 93 | http-ssl-port: 443 |
| 93 | 94 | # [可选] zlm服务器的hook.admin_params=secret |
| 94 | - secret: ZLh24VioqaKqxiihBJ80ny60NuWpVe62 | |
| 95 | + secret: RPorcBlIw26uHGnEHYGesIYyFDXpgjkP1 | |
| 95 | 96 | # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 |
| 96 | 97 | rtp: |
| 97 | 98 | # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 |
| 98 | 99 | enable: true |
| 99 | 100 | # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功 |
| 100 | - port-range: 50000,50300 # 端口范围 | |
| 101 | + port-range: 60000,60300 # 端口范围 | |
| 101 | 102 | # [可选] 国标级联在此范围内选择端口发送媒体流, |
| 102 | - send-port-range: 50000,50300 # 端口范围 | |
| 103 | + send-port-range: 60000,60300 # 端口范围 | |
| 103 | 104 | # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 |
| 104 | 105 | record-assist-port: 18081 |
| 105 | 106 | # [根据业务需求配置] |
| ... | ... | @@ -111,17 +112,13 @@ user-settings: |
| 111 | 112 | # 设备/通道状态变化时发送消息 |
| 112 | 113 | device-status-notify: true |
| 113 | 114 | # 推流直播是否录制 |
| 114 | -# record-push-live: true | |
| 115 | + # record-push-live: true | |
| 115 | 116 | # 是否开启接口鉴权 |
| 116 | -# interface-authentication: false | |
| 117 | + # interface-authentication: false | |
| 117 | 118 | # 国标是否录制 |
| 118 | -# record-sip: true | |
| 119 | + # record-sip: true | |
| 119 | 120 | # 等待音视频编码信息再返回, true: 可以根据编码选择合适的播放器,false: 可以更快点播 |
| 120 | 121 | # wait-track: true |
| 121 | 122 | # [可选] 日志配置, 一般不需要改 |
| 122 | 123 | logging: |
| 123 | - config: classpath:logback-spring.xml | |
| 124 | -#mybatis: | |
| 125 | -# configuration: | |
| 126 | -# log-impl: org.apache.ibatis.logging.stdout.StdOutImpl | |
| 127 | - | |
| 124 | + config: classpath:logback-spring.xml | |
| 128 | 125 | \ No newline at end of file | ... | ... |
web_src/config/index.js
web_src/src/components/channelList.vue
| ... | ... | @@ -25,6 +25,7 @@ |
| 25 | 25 | <el-option label="全部" value=""></el-option> |
| 26 | 26 | <el-option label="在线" value="true"></el-option> |
| 27 | 27 | <el-option label="离线" value="false"></el-option> |
| 28 | + <el-option label="特定通道" value="the"></el-option> | |
| 28 | 29 | </el-select> |
| 29 | 30 | 码流类型重置: |
| 30 | 31 | <el-select size="mini" style="width: 16rem; margin-right: 1rem;" @change="subStreamChange" v-model="subStream" |
| ... | ... | @@ -242,7 +243,7 @@ export default { |
| 242 | 243 | updateLooper: 0, //数据刷新轮训标志 |
| 243 | 244 | searchSrt: "", |
| 244 | 245 | channelType: "", |
| 245 | - online: "", | |
| 246 | + online: "the", | |
| 246 | 247 | subStream: "", |
| 247 | 248 | winHeight: window.innerHeight - 200, |
| 248 | 249 | currentPage: 1, | ... | ... |