Compare commits

...

3 Commits

Author SHA1 Message Date
阿斌
ed15eca319
Pre Merge pull request !36 from 阿斌/N/A 2026-06-23 04:24:34 +00:00
lin
391fdf4660 增加看守位控制 2026-06-23 12:20:40 +08:00
阿斌
da98101aac
update src/main/resources/civilCode.csv.
行政规划错误。江苏南通海门市,修改为海门区,浙江杭州删除下城区、江干区,新增钱塘区,临平区

Signed-off-by: 阿斌 <38912748@qq.com>
2024-12-15 08:58:42 +00:00
9 changed files with 176 additions and 10 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 攻击 // 如果为 true默认作则堆栈将保持套接字打开以便以牺牲线程和内存资源为代价来最大化性能 - 使自身容易受到 DOS 攻击
properties.setProperty("gov.nist.javax.sip.CACHE_SERVER_CONNECTIONS", String.valueOf(sipCacheServerConnections)); 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 * 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); 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 = "deviceId", description = "设备国标编号", required = true)
@Parameter(name = "channelId", description = "通道国标编号", required = true) @Parameter(name = "channelId", description = "通道国标编号", required = true)
@Parameter(name = "enabled", 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 resetTime,
@RequestParam(required = false) Integer presetIndex) { @RequestParam(required = false) Integer presetIndex) {
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("看守位控制API调用"); log.debug("看守位设置API调用");
} }
Device device = deviceService.getDeviceByDeviceId(deviceId); Device device = deviceService.getDeviceByDeviceId(deviceId);
Assert.notNull(device, "设备不存在"); Assert.notNull(device, "设备不存在");
@ -154,7 +154,7 @@ public class DeviceControl {
result.setResult(new WVPResult<>(code, msg, data)); result.setResult(new WVPResult<>(code, msg, data));
}); });
result.onTimeout(() -> { result.onTimeout(() -> {
log.warn("[看守位控制] 操作超时, 设备未返回应答指令, {}", deviceId); log.warn("[看守位设置] 操作超时, 设备未返回应答指令, {}", deviceId);
result.setResult(WVPResult.fail(ErrorCode.ERROR100.getCode(), "操作超时, 设备未应答")); result.setResult(WVPResult.fail(ErrorCode.ERROR100.getCode(), "操作超时, 设备未应答"));
}); });
return result; return result;

View File

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

View File

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

@ -861,7 +861,7 @@
320623,如东县,3206 320623,如东县,3206
320681,启东市,3206 320681,启东市,3206
320682,如皋市,3206 320682,如皋市,3206
320684,海门,3206 320684,海门,3206
320685,海安市,3206 320685,海安市,3206
3207,连云港市,32 3207,连云港市,32
320703,连云区,3207 320703,连云区,3207
@ -918,8 +918,6 @@
33,浙江省, 33,浙江省,
3301,杭州市,33 3301,杭州市,33
330102,上城区,3301 330102,上城区,3301
330103,下城区,3301
330104,江干区,3301
330105,拱墅区,3301 330105,拱墅区,3301
330106,西湖区,3301 330106,西湖区,3301
330108,滨江区,3301 330108,滨江区,3301
@ -927,6 +925,8 @@
330110,余杭区,3301 330110,余杭区,3301
330111,富阳区,3301 330111,富阳区,3301
330112,临安区,3301 330112,临安区,3301
330113,临平区,3301
330114,钱塘区,3301
330122,桐庐县,3301 330122,桐庐县,3301
330127,淳安县,3301 330127,淳安县,3301
330182,建德市,3301 330182,建德市,3301

1 编号 名称 上级
861 320623 如东县 3206
862 320681 启东市 3206
863 320682 如皋市 3206
864 320684 海门市 海门区 3206
865 320685 海安市 3206
866 3207 连云港市 32
867 320703 连云区 3207
918 33 浙江省
919 3301 杭州市 33
920 330102 上城区 3301
330103 下城区 3301
330104 江干区 3301
921 330105 拱墅区 3301
922 330106 西湖区 3301
923 330108 滨江区 3301
925 330110 余杭区 3301
926 330111 富阳区 3301
927 330112 临安区 3301
928 330113 临平区 3301
929 330114 钱塘区 3301
930 330122 桐庐县 3301
931 330127 淳安县 3301
932 330182 建德市 3301

View File

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