增加看守位控制

This commit is contained in:
lin 2026-06-23 12:20:40 +08:00
parent b71a057b23
commit 391fdf4660
8 changed files with 173 additions and 7 deletions

View File

@ -0,0 +1,24 @@
package com.genersoft.iot.vmp.gb28181.conf;
import gov.nist.javax.sip.stack.CustomNioTcpMessageProcessor;
import gov.nist.javax.sip.stack.MessageProcessor;
import gov.nist.javax.sip.stack.NioMessageProcessorFactory;
import gov.nist.javax.sip.stack.SIPTransactionStack;
import javax.sip.ListeningPoint;
import java.io.IOException;
import java.net.InetAddress;
public class CustomMessageProcessorFactory extends NioMessageProcessorFactory {
@Override
public MessageProcessor createMessageProcessor(
SIPTransactionStack sipStack, InetAddress ipAddress,
int port, String transport) throws IOException {
if (transport.equalsIgnoreCase(ListeningPoint.TCP)) {
return new CustomNioTcpMessageProcessor(ipAddress, sipStack, port);
}
return super.createMessageProcessor(sipStack, ipAddress, port, transport);
}
}

View File

@ -51,7 +51,7 @@ public class DefaultProperties {
// 如果为 true默认作则堆栈将保持套接字打开以便以牺牲线程和内存资源为代价来最大化性能 - 使自身容易受到 DOS 攻击
properties.setProperty("gov.nist.javax.sip.CACHE_SERVER_CONNECTIONS", String.valueOf(sipCacheServerConnections));
properties.setProperty("gov.nist.javax.sip.MESSAGE_PROCESSOR_FACTORY", "gov.nist.javax.sip.stack.NioMessageProcessorFactory");
properties.setProperty("gov.nist.javax.sip.MESSAGE_PROCESSOR_FACTORY", "com.genersoft.iot.vmp.gb28181.conf.CustomMessageProcessorFactory");
/**
* sip_server_log.log sip_debug_log.log ERROR, INFO, WARNING, OFF, DEBUG, TRACE

View File

@ -134,7 +134,7 @@ public class DeviceControl {
deviceService.iFrame(device, channelId);
}
@Operation(summary = "看守位控制", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Operation(summary = "看守位设置", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "enabled", description = "是否开启看守位", required = true)
@ -145,7 +145,7 @@ public class DeviceControl {
@RequestParam(required = false) Integer resetTime,
@RequestParam(required = false) Integer presetIndex) {
if (log.isDebugEnabled()) {
log.debug("看守位控制API调用");
log.debug("看守位设置API调用");
}
Device device = deviceService.getDeviceByDeviceId(deviceId);
Assert.notNull(device, "设备不存在");
@ -154,7 +154,7 @@ public class DeviceControl {
result.setResult(new WVPResult<>(code, msg, data));
});
result.onTimeout(() -> {
log.warn("[看守位控制] 操作超时, 设备未返回应答指令, {}", deviceId);
log.warn("[看守位设置] 操作超时, 设备未返回应答指令, {}", deviceId);
result.setResult(WVPResult.fail(ErrorCode.ERROR100.getCode(), "操作超时, 设备未应答"));
});
return result;

View File

@ -1294,7 +1294,7 @@ public class DeviceServiceImpl implements IDeviceService {
try {
sipCommander.homePositionCmd(device, channelId, enabled, resetTime, presetIndex, callback);
} catch (InvalidArgumentException | SipException | ParseException e) {
log.error("[命令发送失败] 看守位控制: {}", e.getMessage());
log.error("[命令发送失败] 看守位设置: {}", e.getMessage());
callback.run(ErrorCode.ERROR100.getCode(), "命令发送: " + e.getMessage(), null);
throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
}

View File

@ -778,7 +778,7 @@ public class SIPCommander implements ISIPCommander {
}
/**
* 看守位控制命令
* 看守位设置
*
* @param device 视频设备
* @param channelId 通道id非通道则是设备本身

View File

@ -0,0 +1,34 @@
package gov.nist.javax.sip.stack;
import gov.nist.core.HostPort;
import java.io.IOException;
import java.net.InetAddress;
public class CustomNioTcpMessageProcessor extends NioTcpMessageProcessor {
public CustomNioTcpMessageProcessor(InetAddress ipAddress,
SIPTransactionStack sipStack, int port) {
super(ipAddress, sipStack, port);
}
@Override
public MessageChannel createMessageChannel(HostPort targetHostPort) throws IOException {
MessageChannel retval = null;
try {
String key = MessageChannel.getKey(targetHostPort, transport);
retval = messageChannels.get(key);
//here we use double-checked locking trying to reduce contention
} finally {
}
return retval;
}
@Override
public MessageChannel createMessageChannel(InetAddress targetHost, int port) throws IOException {
String key = MessageChannel.getKey(targetHost, port, transport);
MessageChannel retval = messageChannels.get(key);
//here we use double-checked locking trying to reduce contention
return retval;
}
}

View File

@ -16,6 +16,10 @@
<i class="iconfont icon-slider-right" style="margin-right: 6px" />
<span>线性扫描</span>
</el-menu-item>
<el-menu-item index="guard">
<i class="el-icon-s-home" style="margin-right: 6px" />
<span>看守位</span>
</el-menu-item>
<el-menu-item index="switch">
<i class="el-icon-s-tools" style="margin-right: 6px" />
<span>辅助开关</span>
@ -30,6 +34,7 @@
<ptzPresetConfig v-if="activeTab === 'preset'" :device-id="deviceId" :channel-device-id="channelDeviceId" />
<ptzCruiseConfig v-if="activeTab === 'cruise'" :device-id="deviceId" :channel-device-id="channelDeviceId" />
<ptzScanConfig v-if="activeTab === 'scan'" :device-id="deviceId" :channel-device-id="channelDeviceId" />
<ptzGuardConfig v-if="activeTab === 'guard'" :device-id="deviceId" :channel-device-id="channelDeviceId" />
<ptzSwitchConfig v-if="activeTab === 'switch'" :device-id="deviceId" :channel-device-id="channelDeviceId" />
</div>
</div>
@ -43,10 +48,11 @@ import ptzPresetConfig from '../common/ptzPresetConfig.vue'
import ptzCruiseConfig from '../common/ptzCruiseConfig.vue'
import ptzScanConfig from '../common/ptzScanConfig.vue'
import ptzSwitchConfig from '../common/ptzSwitchConfig.vue'
import ptzGuardConfig from '../common/ptzGuardConfig.vue'
export default {
name: 'PtzConfigPage',
components: { playerPtzPanel, ptzPresetConfig, ptzCruiseConfig, ptzScanConfig, ptzSwitchConfig },
components: { playerPtzPanel, ptzPresetConfig, ptzCruiseConfig, ptzScanConfig, ptzSwitchConfig, ptzGuardConfig },
props: {
deviceId: { type: String, default: null },
channelDeviceId: { type: String, default: null }

View File

@ -0,0 +1,102 @@
<template>
<div>
<el-form label-width="120px" class="guard-form">
<el-form-item label="启用">
<el-switch v-model="enabled" />
</el-form-item>
<el-form-item label="预置位">
<el-select v-model="presetIndex" style="width: 180px" placeholder="选择预置位">
<el-option v-for="p in allPresetList" :key="p.presetId"
:label="p.presetId + '-' + (p.presetName || ('预置点' + p.presetId))"
:value="Number(p.presetId)" />
</el-select>
</el-form-item>
<el-form-item label="自动归位(秒)">
<el-input-number v-model="resetTime" :min="1" :max="999999" controls-position="right" style="width: 180px" />
</el-form-item>
<el-form-item>
<div class="guard-actions">
<el-button @click="loadPresets">刷新</el-button>
<el-button type="primary" :loading="submitting" :disabled="submitting" @click="confirmSave">保存</el-button>
</div>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: 'PtzGuardConfig',
props: {
deviceId: { type: String, default: null },
channelDeviceId: { type: String, default: null }
},
data() {
return {
enabled: false,
presetIndex: null,
resetTime: 10,
allPresetList: [],
submitting: false
}
},
created() {
this.loadPresets()
},
methods: {
loadPresets() {
this.$store.dispatch('frontEnd/queryPreset', [this.deviceId, this.channelDeviceId])
.then(data => {
this.allPresetList = data || []
})
.catch(error => {
console.log('[看守位] 加载预置点列表失败', error)
})
},
confirmSave() {
if (!this.enabled && !this.presetIndex) {
this.$message({ showClose: true, message: '请选择预置位编号', type: 'warning' })
return
}
if (this.resetTime == null || this.resetTime < 1) {
this.$message({ showClose: true, message: '请输入有效的归位时间', type: 'warning' })
return
}
this.submitting = true
const params = {
deviceId: this.deviceId,
channelId: this.channelDeviceId,
enabled: this.enabled
}
if (this.presetIndex != null) {
params.presetIndex = this.presetIndex
}
if (this.resetTime != null) {
params.resetTime = this.resetTime
}
this.$store.dispatch('device/homePosition', params)
.then(() => {
this.$message({ showClose: true, message: '保存成功', type: 'success' })
})
.catch(error => {
this.$message({ showClose: true, message: error || '保存失败', type: 'error' })
})
.finally(() => {
this.submitting = false
})
}
}
}
</script>
<style scoped>
.guard-form {
padding: 16px 12px;
max-width: 420px;
}
.guard-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
}
</style>