Pre Merge pull request !40 from 梁春迎/2.7.3

This commit is contained in:
梁春迎 2025-08-18 00:44:35 +00:00 committed by Gitee
commit 3118e60a90
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
149 changed files with 5629 additions and 2860 deletions

View File

@ -46,6 +46,7 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
# 功能特性 # 功能特性
- [X] 集成web界面 - [X] 集成web界面
- [X] 兼容性良好 - [X] 兼容性良好
- [X] 跨平台服务,一次编译多端部署, 可以同时用于x86和arm架构
- [X] 接入设备 - [X] 接入设备
- [X] 视频预览 - [X] 视频预览
- [X] 支持主码流子码流切换 - [X] 支持主码流子码流切换

115
bin/wvp.sh Normal file
View File

@ -0,0 +1,115 @@
#!/bin/bash
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m'
function log() {
message="[Polaris Log]: $1 "
case "$1" in
*"Fail"* | *"Error"* | *"请使用 root 或 sudo 权限运行此脚本"*)
echo -e "${RED}${message}${NC}" 2>&1 | tee -a
;;
*"Success"*)
echo -e "${GREEN}${message}${NC}" 2>&1 | tee -a
;;
*"Ignore"* | *"Jump"*)
echo -e "${YELLOW}${message}${NC}" 2>&1 | tee -a
;;
*)
echo -e "${BLUE}${message}${NC}" 2>&1 | tee -a
;;
esac
}
echo
cat <<EOF
██████╗ ██████╗ ██╗ █████╗ ██████╗ ██╗███████╗
██╔══██╗██╔═══██╗██║ ██╔══██╗██╔══██╗██║██╔════╝
██████╔╝██║ ██║██║ ███████║██████╔╝██║███████╗
██╔═══╝ ██║ ██║██║ ██╔══██║██╔══██╗██║╚════██║
██║ ╚██████╔╝███████╗██║ ██║██║ ██║██║███████║
╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚══════╝
EOF
#配置jdk的路径
export JAVA_HOME=/usr/local/java/jdk1.8.0_202 #此处为JDK路径
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
# WVP-pro defines
AppName=wvp-pro-2.7.2-05131055.jar
AppHome="/root/polaris/wvp/"
# JVM参数
JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512m -Xmx2048m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=1024m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
function start() {
log "======================= 开启流媒体服务 ======================="
log "AppName: $AppName"
log "AppHome: $AppHome"
log "Success:流媒体服务开启成功"
}
function stop() {
log "======================= 停止流媒体服务 ======================="
PID=""
query() {
PID=$(ps -ef | grep java | grep $AppName | grep -v grep | awk '{print $2}')
}
query
if [ x"$PID" != x"" ]; then
log "进程PID: $PID"
kill -TERM $PID
log "$AppName (pid:$PID) exiting..."
while [ x"$PID" != x"" ]; do
sleep 1
query
done
log "Success:$AppName exited."
else
log "Jump:进程不存在"
fi
}
function status() {
log "======================= 运行状态 ======================="
log ""
PID=$(ps -ef | grep java | grep $AppName | grep -v grep | wc -l)
if [ $PID != 0 ]; then
log "进程PID: $PID"
log "$AppName is running..."
else
log "$AppName is not running..."
fi
log ""
log "========================================================"
}
function restart() {
stop
sleep 3
start
}
case $1 in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
status)
status
;;
*) ;;
esac

44
docker/build.sh Executable file
View File

@ -0,0 +1,44 @@
#/bin/bash
set -e
version=2.7.3
git clone https://gitee.com/pan648540858/wvp-GB28181-pro.git
cd wvp-GB28181-pro/web_src && \
npm install && \
npm run build
cd ../../
cp -r wvp-GB28181-pro/src/main/resources/static/* ./nginx/dist
echo "构建ZLM容器"
cd ./media/
chmod +x ./build.sh
./build.sh
cd ../
echo "构建数据库容器"
cd ./mysql/
chmod +x ./build.sh
./build.sh
cd ../
echo "构建Redis容器"
cd ./redis/
chmod +x ./build.sh
./build.sh
cd ../
echo "构建WVP容器"
cd ./wvp/
chmod +x ./build.sh
./build.sh
cd ../
echo "构建Nginx容器"
cd ./nginx/
chmod +x ./build.sh
./build.sh
cd ../
./push.sh

View File

@ -1,49 +1,125 @@
version: '3' version: '3'
services: services:
redis: polaris-redis:
image: redis image: polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-redis:latest
restart: always restart: unless-stopped
volumes: healthcheck:
- ./redis/redis.conf:/etc/redis/redis_default.conf test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ]
- ./redis/data/:/data interval: 15s
environment: timeout: 5s
TZ: "Asia/Shanghai" retries: 10
command: redis-server /etc/redis/redis_default.conf --appendonly yes start_period: 10s
wvp: networks:
build: - media-net
context: ./wvp
args:
gitUrl: "https://gitee.com/pan648540858"
zlmGitUrl: "https://gitee.com/xia-chu/ZLMediaKit"
restart: always
ports: ports:
- "5060:5060" - 6379:6379
- "5060:5060/udp"
- "18080:18080"
- "80:80"
- "10000:10000/tcp"
- "10000:10000/udp"
- "30000-30500:30000-30500/tcp"
- "30000-30500:30000-30500/udp"
volumes: volumes:
- ./video:/opt/media/www/record/ - ./redis/conf/redis.conf:/opt/polaris/redis/redis.conf
- ./logs/wvp:/opt/wvp/logs/ - ./volumes/redis/data/:/data
- ./logs/assist:/opt/assist/logs/
- ./logs/media:/opt/media/log/
environment: environment:
TZ: "Asia/Shanghai" TZ: "Asia/Shanghai"
# [必须修改] 本机的IP command: redis-server /opt/polaris/redis/redis.conf --appendonly yes
WVP_HOST: 172.18.0.61
WVP_PWD: aseqw_+hiy123 polaris-mysql:
WVP_DOMAIN: 6101130049 image: polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-mysql:latest
WVP_ID: 61011300490000000001 restart: unless-stopped
REDIS_HOST: redis healthcheck:
REDIS_PORT: 6379 test: [ "CMD", "bash", "-c", "cat < /dev/null > /dev/tcp/127.0.0.1/3306" ]
REDIS_DB: 6 interval: 15s
REDIS_PWD: root timeout: 5s
ASSIST_JVM_CONFIG: -Xms128m -Xmx256m retries: 10
WVP_JVM_CONFIG: -Xms128m -Xmx256m start_period: 10s
ASSIST_CONFIG: networks:
WVP_CONFIG: - media-net
environment:
MYSQL_DATABASE: wvp
MYSQL_ROOT_PASSWORD: root
MYSQL_USER: root
MYSQL_PASSWORD: root
TZ: Asia/Shanghai
ports:
- 3306:3306
volumes:
- ./mysql/conf:/etc/mysql/conf.d
- ./logs/mysql:/logs
- ./volumes/mysql/data:/var/lib/mysql
command: [
'mysqld',
'--default-authentication-plugin=mysql_native_password',
'--innodb-buffer-pool-size=80M',
'--character-set-server=utf8mb4',
'--collation-server=utf8mb4_general_ci',
'--default-time-zone=+8:00',
'--lower-case-table-names=1'
]
polaris-media:
image: polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-media:latest
restart: always
networks:
- media-net
ports:
- "10935:10935"
- "5540:5540"
- "6080:6080"
volumes:
- ./volumes/video:/opt/media/www/record/
- ./logs/media:/opt/media/log/
polaris-wvp:
image: polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-wvp:latest
restart: always
networks:
- media-net
ports:
- "18978:18978"
- "8116:8116/udp"
- "8116:8116/tcp"
depends_on: depends_on:
- redis - polaris-redis
- polaris-mysql
- polaris-media
links:
- polaris-redis
- polaris-mysql
- polaris-media
volumes:
- ./wvp/wvp/:/opt/wvp/wvp/
- ./logs/wvp:/opt/wvp/logs/
environment:
TZ: "Asia/Shanghai"
# 本机的IP
SIP_HOST: 127.0.0.1
STREAM_HOST: 127.0.0.1
ZLM_HOST: polaris-media
ZLM_PORT: 6080
ZLM_SERCERT: su6TiedN2rVAmBbIDX0aa0QTiBJLBdcf
REDIS_HOST: polaris-redis
REDIS_PORT: 6379
DATABASE_HOST: polaris-mysql
DATABASE_PORT: 3306
DATABASE_USER: wvp
DATABASE_PASSWORD: wvp
# 前端跨域配置nginx容器所在物理机IP
NGINX_HOST: http://127.0.0.1:8080
polaris-nginx:
image: polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-nginx:latest
ports:
- "8080:8080"
depends_on:
- polaris-wvp
links:
- polaris-wvp
environment:
WVP_HOST: polaris-wvp
WVP_PORT: 18978
volumes:
- ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf
- ./logs/nginx:/var/log/nginx
networks:
- media-net
networks:
media-net:
driver: bridge

5
docker/docker-upgrade.sh Executable file
View File

@ -0,0 +1,5 @@
#/bin/bash
set -e
docker compose down
docker compose up -d --remove-orphans

91
docker/media/Dockerfile Normal file
View File

@ -0,0 +1,91 @@
FROM ubuntu:20.04 AS build
#shell,rtmp,rtsp,rtsps,http,rtp
EXPOSE 10935/tcp
EXPOSE 5540/tcp
EXPOSE 6080/tcp
EXPOSE 10000/udp
EXPOSE 10000/tcp
EXPOSE 8000/udp
EXPOSE 8000/tcp
EXPOSE 9000/udp
# ADD sources.list /etc/apt/sources.list
RUN apt-get update && \
DEBIAN_FRONTEND="noninteractive" \
apt-get install -y --no-install-recommends \
build-essential \
cmake \
git \
curl \
vim \
wget \
ca-certificates \
tzdata \
libssl-dev \
gcc \
g++ \
gdb && \
apt-get autoremove -y && \
apt-get clean -y && \
rm -rf /var/lib/apt/lists/*
RUN mkdir -p /opt/media
WORKDIR /opt/media
RUN git clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit && \
cd ZLMediaKit && git submodule update --init
# 3rdpart init
WORKDIR /opt/media/ZLMediaKit/3rdpart
RUN wget https://polaris-tian-generic.pkg.coding.net/qt/dependencies/openssl-1.1.1k.tar.gz?version=latest -O openssl-1.1.1k.tar.gz && \
tar -xvzf openssl-1.1.1k.tar.gz && \
cd openssl-1.1.1k && ./config shared --openssldir=/usr/local/openssl --prefix=/usr/local/openssl && \
make && make install && \
echo "/usr/local/lib64/" >> /etc/ld.so.conf && \
echo "/usr/local/openssl/lib" >> /etc/ld.so.conf && \
ldconfig && \
ln -s /usr/local/openssl/bin/openssl /usr/local/bin/openssl
WORKDIR /opt/media/ZLMediaKit/3rdpart
RUN wget https://github.com/cisco/libsrtp/archive/v2.3.0.tar.gz -O libsrtp-2.3.0.tar.gz && \
tar xfv libsrtp-2.3.0.tar.gz && \
mv libsrtp-2.3.0 libsrtp && \
cd libsrtp && ./configure --enable-openssl --with-openssl-dir=/usr/local/openssl && make -j $(nproc) && make install
WORKDIR /opt/media/ZLMediaKit/build
RUN cmake .. -DENABLE_WEBRTC=true -DOPENSSL_ROOT_DIR=/usr/local/openssl -DOPENSSL_LIBRARIES=/usr/local/openssl/lib && \
cmake --build . --target MediaServer
COPY config.ini /opt/media/ZLMediaKit/release/linux/Debug/
FROM ubuntu:20.04
RUN apt-get update && \
DEBIAN_FRONTEND="noninteractive" \
apt-get install -y --no-install-recommends \
vim \
wget \
ca-certificates \
tzdata \
curl \
libssl-dev \
ffmpeg \
gcc \
g++ \
gdb && \
apt-get autoremove -y && \
apt-get clean -y && \
rm -rf /var/lib/apt/lists/*
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \
&& echo $TZ > /etc/timezone && \
mkdir -p /opt/media/bin/www
WORKDIR /opt/media/bin/
COPY --from=build /opt/media/ZLMediaKit/release/linux/Debug/MediaServer /opt/media/ZLMediaKit/default.pem /opt/media/bin/
COPY --from=build /opt/media/ZLMediaKit/release/linux/Debug/config.ini /opt/media/conf/
COPY --from=build /opt/media/ZLMediaKit/www/ /opt/media/bin/www/
ENV PATH /opt/media/bin:$PATH
CMD ["./MediaServer","-s", "default.pem", "-c", "../conf/config.ini", "-l","0"]

8
docker/media/build.sh Executable file
View File

@ -0,0 +1,8 @@
#/bin/bash
set -e
version=2.7.3
docker build -t polaris-media:${version} .
docker tag polaris-media:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-media:${version}
docker tag polaris-media:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-media:latest

196
docker/media/config.ini Normal file
View File

@ -0,0 +1,196 @@
; auto-generated by mINI class {
[api]
apiDebug=1
defaultSnap=./www/logo.png
downloadRoot=./www;
secret=su6TiedN2rVAmBbIDX0aa0QTiBJLBdcf
snapRoot=./www/snap/
[cluster]
origin_url=
retry_count=3
timeout_sec=15
[ffmpeg]
bin=/usr/bin/ffmpeg
cmd=%s -re -i %s -c:a aac -strict -2 -ar 44100 -ab 48k -c:v libx264 -f flv %s
log=./ffmpeg/ffmpeg.log
restart_sec=0
snap=%s -rtsp_transport tcp -i %s -y -f mjpeg -frames:v 1 %s
[general]
broadcast_player_count_changed=0
check_nvidia_dev=1
enableVhost=0
enable_ffmpeg_log=0
flowThreshold=1024
listen_ip=::
maxStreamWaitMS=15000
mediaServerId=polaris
mergeWriteMS=0
resetWhenRePlay=1
streamNoneReaderDelayMS=20000
unready_frame_cache=100
wait_add_track_ms=3000
wait_audio_track_data_ms=1000
wait_track_ready_ms=10000
[hls]
broadcastRecordTs=0
deleteDelaySec=10
fastRegister=0
fileBufSize=65536
segDelay=0
segDur=2
segKeep=0
segNum=3
segRetain=5
[hook]
alive_interval=10.0
enable=1
on_flow_report=
on_http_access=
on_play=
on_publish=
on_record_mp4=
on_record_ts=
on_rtp_server_timeout=
on_rtsp_auth=
on_rtsp_realm=
on_send_rtp_stopped=
on_server_exited=
on_server_keepalive=
on_server_started=
on_shell_login=
on_stream_changed=
on_stream_none_reader=
on_stream_not_found=
retry=1
retry_delay=3.0
stream_changed_schemas=rtsp/rtmp/fmp4/ts/hls/hls.fmp4
timeoutSec=30
[http]
allow_cross_domains=1
allow_ip_range=
charSet=utf-8
dirMenu=1
forbidCacheSuffix=
forwarded_ip_header=
keepAliveSecond=30
maxReqSize=40960
notFound=<html><head><title>404 Not Found</title></head><body bgcolor="white"><center><h1>您访问的资源不存在!</h1></center><hr><center>ZLMediaKit(git hash:8ccb4e9/%aI,branch:master,build time:2024-11-07T10:34:19)</center></body></html>
port=6080
rootPath=./www
sendBufSize=65536
sslport=4443
virtualPath=
[multicast]
addrMax=239.255.255.255
addrMin=239.0.0.0
udpTTL=64
[protocol]
add_mute_audio=1
auto_close=0
continue_push_ms=3000
enable_audio=1
enable_fmp4=1
enable_hls=1
enable_hls_fmp4=0
enable_mp4=0
enable_rtmp=1
enable_rtsp=1
enable_ts=1
fmp4_demand=0
hls_demand=0
hls_save_path=./www
modify_stamp=2
mp4_as_player=0
mp4_max_second=3600
mp4_save_path=/home
paced_sender_ms=0
rtmp_demand=0
rtsp_demand=0
ts_demand=0
[record]
appName=record
enableFmp4=0
fastStart=0
fileBufSize=65536
fileRepeat=0
sampleMS=500
[rtc]
datachannel_echo=0
externIP=
maxRtpCacheMS=5000
maxRtpCacheSize=2048
max_bitrate=0
min_bitrate=0
nackIntervalRatio=1.0
nackMaxCount=15
nackMaxMS=3000
nackMaxSize=2048
nackRtpSize=8
port=8000
preferredCodecA=PCMA,PCMU,opus,mpeg4-generic
preferredCodecV=H264,H265,AV1,VP9,VP8
rembBitRate=0
start_bitrate=0
tcpPort=8000
timeoutSec=30
[rtmp]
directProxy=1
enhanced=0
handshakeSecond=15
keepAliveSecond=15
port=10935
sslport=0
[rtp]
audioMtuSize=600
h264_stap_a=1
lowLatency=0
rtpMaxSize=10
videoMtuSize=1400
[rtp_proxy]
dumpDir=
gop_cache=1
h264_pt=98
h265_pt=99
opus_pt=100
port=10000
port_range=30000-30500
ps_pt=96
rtp_g711_dur_ms=100
timeoutSec=15
udp_recv_socket_buffer=4194304
[rtsp]
authBasic=0
directProxy=1
handshakeSecond=15
keepAliveSecond=15
lowLatency=0
port=5540
rtpTransportType=-1
sslport=0
[shell]
maxReqSize=1024
port=0
[srt]
latencyMul=4
pktBufSize=8192
port=9000
timeoutSec=5
; } ---

3
docker/mysql/Dockerfile Normal file
View File

@ -0,0 +1,3 @@
FROM mysql:8.0.32
ADD ./db/*.sql /docker-entrypoint-initdb.d/

8
docker/mysql/build.sh Executable file
View File

@ -0,0 +1,8 @@
#/bin/bash
set -e
version=2.7.3
docker build -t polaris-mysql:${version} .
docker tag polaris-mysql:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-mysql:${version}
docker tag polaris-mysql:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-mysql:latest

View File

@ -0,0 +1,3 @@
use mysql;
grant all privileges on wvp.* to 'ylcx'@'%';
flush privileges;

769
docker/mysql/db/wvp.sql Normal file
View File

@ -0,0 +1,769 @@
/*建库*/
DROP DATABASE IF EXISTS `wvp`;
CREATE DATABASE `wvp` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
USE `wvp`;
/*建表*/
drop table IF EXISTS wvp_device;
create table IF NOT EXISTS wvp_device
(
id serial primary key,
device_id character varying(50) not null,
name character varying(255),
manufacturer character varying(255),
model character varying(255),
firmware character varying(255),
transport character varying(50),
stream_mode character varying(50),
on_line bool default false,
register_time character varying(50),
keepalive_time character varying(50),
ip character varying(50),
create_time character varying(50),
update_time character varying(50),
port integer,
expires integer,
subscribe_cycle_for_catalog integer DEFAULT 0,
subscribe_cycle_for_mobile_position integer DEFAULT 0,
mobile_position_submission_interval integer DEFAULT 5,
subscribe_cycle_for_alarm integer DEFAULT 0,
host_address character varying(50),
charset character varying(50),
ssrc_check bool default false,
geo_coord_sys character varying(50),
media_server_id character varying(50) default 'auto',
custom_name character varying(255),
sdp_ip character varying(50),
local_ip character varying(50),
password character varying(255),
as_message_channel bool default false,
heart_beat_interval integer,
heart_beat_count integer,
position_capability integer,
broadcast_push_after_ack bool default false,
server_id character varying(50),
constraint uk_device_device unique (device_id)
);
drop table IF EXISTS wvp_device_alarm;
create table IF NOT EXISTS wvp_device_alarm
(
id serial primary key,
device_id character varying(50) not null,
channel_id character varying(50) not null,
alarm_priority character varying(50),
alarm_method character varying(50),
alarm_time character varying(50),
alarm_description character varying(255),
longitude double precision,
latitude double precision,
alarm_type character varying(50),
create_time character varying(50) not null
);
drop table IF EXISTS wvp_device_mobile_position;
create table IF NOT EXISTS wvp_device_mobile_position
(
id serial primary key,
device_id character varying(50) not null,
channel_id character varying(50) not null,
device_name character varying(255),
time character varying(50),
longitude double precision,
latitude double precision,
altitude double precision,
speed double precision,
direction double precision,
report_source character varying(50),
create_time character varying(50)
);
drop table IF EXISTS wvp_device_channel;
create table IF NOT EXISTS wvp_device_channel
(
id serial primary key,
device_id character varying(50),
name character varying(255),
manufacturer character varying(50),
model character varying(50),
owner character varying(50),
civil_code character varying(50),
block character varying(50),
address character varying(50),
parental integer,
parent_id character varying(50),
safety_way integer,
register_way integer,
cert_num character varying(50),
certifiable integer,
err_code integer,
end_time character varying(50),
secrecy integer,
ip_address character varying(50),
port integer,
password character varying(255),
status character varying(50),
longitude double precision,
latitude double precision,
ptz_type integer,
position_type integer,
room_type integer,
use_type integer,
supply_light_type integer,
direction_type integer,
resolution character varying(255),
business_group_id character varying(255),
download_speed character varying(255),
svc_space_support_mod integer,
svc_time_support_mode integer,
create_time character varying(50) not null,
update_time character varying(50) not null,
sub_count integer,
stream_id character varying(255),
has_audio bool default false,
gps_time character varying(50),
stream_identification character varying(50),
channel_type int default 0 not null,
gb_device_id character varying(50),
gb_name character varying(255),
gb_manufacturer character varying(255),
gb_model character varying(255),
gb_owner character varying(255),
gb_civil_code character varying(255),
gb_block character varying(255),
gb_address character varying(255),
gb_parental integer,
gb_parent_id character varying(255),
gb_safety_way integer,
gb_register_way integer,
gb_cert_num character varying(50),
gb_certifiable integer,
gb_err_code integer,
gb_end_time character varying(50),
gb_secrecy integer,
gb_ip_address character varying(50),
gb_port integer,
gb_password character varying(50),
gb_status character varying(50),
gb_longitude double,
gb_latitude double,
gb_business_group_id character varying(50),
gb_ptz_type integer,
gb_position_type integer,
gb_room_type integer,
gb_use_type integer,
gb_supply_light_type integer,
gb_direction_type integer,
gb_resolution character varying(255),
gb_download_speed character varying(255),
gb_svc_space_support_mod integer,
gb_svc_time_support_mode integer,
record_plan_id integer,
data_type integer not null,
data_device_id integer not null,
gps_speed double precision,
gps_altitude double precision,
gps_direction double precision,
index (data_type),
index (data_device_id),
constraint uk_wvp_unique_channel unique (gb_device_id)
);
drop table IF EXISTS wvp_media_server;
create table IF NOT EXISTS wvp_media_server
(
id character varying(255) primary key,
ip character varying(50),
hook_ip character varying(50),
sdp_ip character varying(50),
stream_ip character varying(50),
http_port integer,
http_ssl_port integer,
rtmp_port integer,
rtmp_ssl_port integer,
rtp_proxy_port integer,
rtsp_port integer,
rtsp_ssl_port integer,
flv_port integer,
flv_ssl_port integer,
ws_flv_port integer,
ws_flv_ssl_port integer,
auto_config bool default false,
secret character varying(50),
type character varying(50) default 'zlm',
rtp_enable bool default false,
rtp_port_range character varying(50),
send_rtp_port_range character varying(50),
record_assist_port integer,
default_server bool default false,
create_time character varying(50),
update_time character varying(50),
hook_alive_interval integer,
record_path character varying(255),
record_day integer default 7,
transcode_suffix character varying(255),
server_id character varying(50),
constraint uk_media_server_unique_ip_http_port unique (ip, http_port, server_id)
);
drop table IF EXISTS wvp_platform;
create table IF NOT EXISTS wvp_platform
(
id serial primary key,
enable bool default false,
name character varying(255),
server_gb_id character varying(50),
server_gb_domain character varying(50),
server_ip character varying(50),
server_port integer,
device_gb_id character varying(50),
device_ip character varying(50),
device_port character varying(50),
username character varying(255),
password character varying(50),
expires character varying(50),
keep_timeout character varying(50),
transport character varying(50),
civil_code character varying(50),
manufacturer character varying(255),
model character varying(255),
address character varying(255),
character_set character varying(50),
ptz bool default false,
rtcp bool default false,
status bool default false,
catalog_group integer,
register_way integer,
secrecy integer,
create_time character varying(50),
update_time character varying(50),
as_message_channel bool default false,
catalog_with_platform integer default 1,
catalog_with_group integer default 1,
catalog_with_region integer default 1,
auto_push_channel bool default true,
send_stream_ip character varying(50),
server_id character varying(50),
constraint uk_platform_unique_server_gb_id unique (server_gb_id)
);
drop table IF EXISTS wvp_platform_channel;
create table IF NOT EXISTS wvp_platform_channel
(
id serial primary key,
platform_id integer,
device_channel_id integer,
custom_device_id character varying(50),
custom_name character varying(255),
custom_manufacturer character varying(50),
custom_model character varying(50),
custom_owner character varying(50),
custom_civil_code character varying(50),
custom_block character varying(50),
custom_address character varying(50),
custom_parental integer,
custom_parent_id character varying(50),
custom_safety_way integer,
custom_register_way integer,
custom_cert_num character varying(50),
custom_certifiable integer,
custom_err_code integer,
custom_end_time character varying(50),
custom_secrecy integer,
custom_ip_address character varying(50),
custom_port integer,
custom_password character varying(255),
custom_status character varying(50),
custom_longitude double precision,
custom_latitude double precision,
custom_ptz_type integer,
custom_position_type integer,
custom_room_type integer,
custom_use_type integer,
custom_supply_light_type integer,
custom_direction_type integer,
custom_resolution character varying(255),
custom_business_group_id character varying(255),
custom_download_speed character varying(255),
custom_svc_space_support_mod integer,
custom_svc_time_support_mode integer,
constraint uk_platform_gb_channel_platform_id_catalog_id_device_channel_id unique (platform_id, device_channel_id),
constraint uk_platform_gb_channel_device_id unique (custom_device_id)
);
drop table IF EXISTS wvp_platform_group;
create table IF NOT EXISTS wvp_platform_group
(
id serial primary key,
platform_id integer,
group_id integer,
constraint uk_wvp_platform_group_platform_id_group_id unique (platform_id, group_id)
);
drop table IF EXISTS wvp_platform_region;
create table IF NOT EXISTS wvp_platform_region
(
id serial primary key,
platform_id integer,
region_id integer,
constraint uk_wvp_platform_region_platform_id_group_id unique (platform_id, region_id)
);
drop table IF EXISTS wvp_stream_proxy;
create table IF NOT EXISTS wvp_stream_proxy
(
id serial primary key,
type character varying(50),
app character varying(255),
stream character varying(255),
src_url character varying(255),
timeout integer,
ffmpeg_cmd_key character varying(255),
rtsp_type character varying(50),
media_server_id character varying(50),
enable_audio bool default false,
enable_mp4 bool default false,
pulling bool default false,
enable bool default false,
enable_remove_none_reader bool default false,
create_time character varying(50),
name character varying(255),
update_time character varying(50),
stream_key character varying(255),
server_id character varying(50),
enable_disable_none_reader bool default false,
relates_media_server_id character varying(50),
constraint uk_stream_proxy_app_stream unique (app, stream)
);
drop table IF EXISTS wvp_stream_push;
create table IF NOT EXISTS wvp_stream_push
(
id serial primary key,
app character varying(255),
stream character varying(255),
create_time character varying(50),
media_server_id character varying(50),
server_id character varying(50),
push_time character varying(50),
status bool default false,
update_time character varying(50),
pushing bool default false,
self bool default false,
start_offline_push bool default true,
constraint uk_stream_push_app_stream unique (app, stream)
);
drop table IF EXISTS wvp_cloud_record;
create table IF NOT EXISTS wvp_cloud_record
(
id serial primary key,
app character varying(255),
stream character varying(255),
call_id character varying(255),
start_time bigint,
end_time bigint,
media_server_id character varying(50),
server_id character varying(50),
file_name character varying(255),
folder character varying(500),
file_path character varying(500),
collect bool default false,
file_size bigint,
time_len bigint
);
drop table IF EXISTS wvp_user;
create table IF NOT EXISTS wvp_user
(
id serial primary key,
username character varying(255),
password character varying(255),
role_id integer,
create_time character varying(50),
update_time character varying(50),
push_key character varying(50),
constraint uk_user_username unique (username)
);
drop table IF EXISTS wvp_user_role;
create table IF NOT EXISTS wvp_user_role
(
id serial primary key,
name character varying(50),
authority character varying(50),
create_time character varying(50),
update_time character varying(50)
);
drop table IF EXISTS wvp_user_api_key;
create table IF NOT EXISTS wvp_user_api_key
(
id serial primary key,
user_id bigint,
app character varying(255),
api_key text,
expired_at bigint,
remark character varying(255),
enable bool default true,
create_time character varying(50),
update_time character varying(50)
);
/*初始数据*/
INSERT INTO wvp_user
VALUES (1, 'admin', '21232f297a57a5a743894a0e4a801fc3', 1, '2021-04-13 14:14:57', '2021-04-13 14:14:57',
'3e80d1762a324d5b0ff636e0bd16f1e3');
INSERT INTO wvp_user_role
VALUES (1, 'admin', '0', '2021-04-13 14:14:57', '2021-04-13 14:14:57');
drop table IF EXISTS wvp_common_group;
create table IF NOT EXISTS wvp_common_group
(
id serial primary key,
device_id varchar(50) NOT NULL,
name varchar(255) NOT NULL,
parent_id int,
parent_device_id varchar(50) DEFAULT NULL,
business_group varchar(50) NOT NULL,
create_time varchar(50) NOT NULL,
update_time varchar(50) NOT NULL,
civil_code varchar(50) default null,
constraint uk_common_group_device_platform unique (device_id)
);
drop table IF EXISTS wvp_common_region;
create table IF NOT EXISTS wvp_common_region
(
id serial primary key,
device_id varchar(50) NOT NULL,
name varchar(255) NOT NULL,
parent_id int,
parent_device_id varchar(50) DEFAULT NULL,
create_time varchar(50) NOT NULL,
update_time varchar(50) NOT NULL,
constraint uk_common_region_device_id unique (device_id)
);
drop table IF EXISTS wvp_record_plan;
create table IF NOT EXISTS wvp_record_plan
(
id serial primary key,
snap bool default false,
name varchar(255) NOT NULL,
create_time character varying(50),
update_time character varying(50)
);
drop table IF EXISTS wvp_record_plan_item;
create table IF NOT EXISTS wvp_record_plan_item
(
id serial primary key,
start int,
stop int,
week_day int,
plan_id int,
create_time character varying(50),
update_time character varying(50)
);
/*
* 20240528
*/
DELIMITER // -- 重定义分隔符避免分号冲突
CREATE PROCEDURE `wvp_20240528`()
BEGIN
IF NOT EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_media_server' and column_name = 'transcode_suffix')
THEN
ALTER TABLE wvp_media_server ADD transcode_suffix character varying(255);
END IF;
IF not EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_media_server' and column_name = 'type')
THEN
alter table wvp_media_server
add type character varying(50) default 'zlm';
END IF;
IF not EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_media_server' and column_name = 'flv_port')
THEN
alter table wvp_media_server add flv_port integer;
END IF;
IF not EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_media_server' and column_name = 'flv_ssl_port')
THEN
alter table wvp_media_server add flv_ssl_port integer;
END IF;
IF not EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_media_server' and column_name = 'ws_flv_port')
THEN
alter table wvp_media_server add ws_flv_port integer;
END IF;
IF not EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_media_server' and column_name = 'ws_flv_ssl_port')
THEN
alter table wvp_media_server add ws_flv_ssl_port integer;
END IF;
END; //
call wvp_20240528();
DROP PROCEDURE wvp_20240528;
DELIMITER ;
create table IF NOT EXISTS wvp_user_api_key (
id serial primary key ,
user_id bigint,
app character varying(255) ,
api_key text,
expired_at bigint,
remark character varying(255),
enable bool default true,
create_time character varying(50),
update_time character varying(50)
);
/*
* 20241222
*/
DELIMITER // -- 重定义分隔符避免分号冲突
CREATE PROCEDURE `wvp_20241222`()
BEGIN
IF EXISTS (SELECT column_name FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and INDEX_NAME = 'uk_wvp_device_channel_unique_device_channel')
THEN
alter table wvp_device_channel drop index uk_wvp_device_channel_unique_device_channel;
END IF;
IF EXISTS (SELECT column_name FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and INDEX_NAME = 'uk_wvp_unique_stream_push_id')
THEN
alter table wvp_device_channel drop index uk_wvp_unique_stream_push_id;
END IF;
IF EXISTS (SELECT column_name FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and INDEX_NAME = 'uk_wvp_unique_stream_proxy_id')
THEN
alter table wvp_device_channel drop index uk_wvp_unique_stream_proxy_id;
END IF;
IF not EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and column_name = 'data_type')
THEN
alter table wvp_device_channel add data_type integer not null;
END IF;
IF not EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and column_name = 'data_device_id')
THEN
alter table wvp_device_channel add data_device_id integer not null;
END IF;
IF EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and column_name = 'device_db_id')
THEN
update wvp_device_channel wdc INNER JOIN
(SELECT id, device_db_id from wvp_device_channel where device_db_id is not null ) ct on ct.id = wdc.id
set wdc.data_type = 1, wdc.data_device_id = ct.device_db_id where wdc.device_db_id is not null;
alter table wvp_device_channel drop device_db_id;
END IF;
IF EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and column_name = 'stream_push_id')
THEN
update wvp_device_channel wdc INNER JOIN
(SELECT id, stream_push_id from wvp_device_channel where stream_push_id is not null ) ct on ct.id = wdc.id
set wdc.data_type = 2, wdc.data_device_id = ct.stream_push_id where wdc.stream_push_id is not null;
alter table wvp_device_channel drop stream_push_id;
END IF;
IF EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and column_name = 'stream_proxy_id')
THEN
update wvp_device_channel wdc INNER JOIN
(SELECT id, stream_proxy_id from wvp_device_channel where stream_proxy_id is not null ) ct on ct.id = wdc.id
set wdc.data_type = 3, wdc.data_device_id = ct.stream_proxy_id where wdc.stream_proxy_id is not null;
alter table wvp_device_channel drop stream_proxy_id;
END IF;
END; //
call wvp_20241222();
DROP PROCEDURE wvp_20241222;
DELIMITER ;
/*
* 20241231
*/
DELIMITER //
CREATE PROCEDURE `wvp_20241231`()
BEGIN
IF not EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_stream_proxy' and column_name = 'relates_media_server_id')
THEN
alter table wvp_stream_proxy add relates_media_server_id character varying(50);
END IF;
END; //
call wvp_20241231();
DROP PROCEDURE wvp_20241231;
DELIMITER ;
/*
* 20250111
*/
DELIMITER // -- 重定义分隔符避免分号冲突
CREATE PROCEDURE `wvp_20250111`()
BEGIN
IF EXISTS (SELECT column_name FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_cloud_record' and INDEX_NAME = 'uk_stream_push_app_stream_path')
THEN
alter table wvp_cloud_record drop index uk_stream_push_app_stream_path ;
END IF;
IF EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_cloud_record' and column_name = 'folder')
THEN
alter table wvp_cloud_record modify folder varchar(500) null;
END IF;
IF EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_cloud_record' and column_name = 'file_path')
THEN
alter table wvp_cloud_record modify file_path varchar(500) null;
END IF;
END; //
call wvp_20250111();
DROP PROCEDURE wvp_20250111;
DELIMITER ;
/*
* 20250211
*/
DELIMITER // -- 重定义分隔符避免分号冲突
CREATE PROCEDURE `wvp_20250211`()
BEGIN
IF EXISTS (SELECT column_name FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device' and column_name = 'keepalive_interval_time')
THEN
alter table wvp_device change keepalive_interval_time heart_beat_interval integer after as_message_channel;
END IF;
IF not EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device' and column_name = 'heart_beat_count')
THEN
alter table wvp_device add heart_beat_count integer;
END IF;
IF not EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device' and column_name = 'position_capability')
THEN
alter table wvp_device add position_capability integer;
END IF;
END; //
call wvp_20250211();
DROP PROCEDURE wvp_20250211;
DELIMITER ;
/**
* 20250312
*/
DELIMITER // -- 重定义分隔符避免分号冲突
CREATE PROCEDURE `wvp_20250312`()
BEGIN
DECLARE serverId VARCHAR(32) DEFAULT '你的服务ID';
IF not EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device' and column_name = 'server_id')
THEN
alter table wvp_device add server_id character varying(50);
update wvp_device set server_id = serverId;
END IF;
IF not EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_media_server' and column_name = 'server_id')
THEN
alter table wvp_media_server add server_id character varying(50);
update wvp_media_server set server_id = serverId;
END IF;
IF not EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_stream_proxy' and column_name = 'server_id')
THEN
alter table wvp_stream_proxy add server_id character varying(50);
update wvp_stream_proxy set server_id = serverId;
END IF;
IF not EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_cloud_record' and column_name = 'server_id')
THEN
alter table wvp_cloud_record add server_id character varying(50);
update wvp_cloud_record set server_id = serverId;
END IF;
IF not EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_platform' and column_name = 'server_id')
THEN
alter table wvp_platform add server_id character varying(50);
END IF;
END; //
call wvp_20250312();
DROP PROCEDURE wvp_20250312;
DELIMITER ;
/*
* 20250319
*/
DELIMITER // -- 重定义分隔符避免分号冲突
CREATE PROCEDURE `wvp_20250319`()
BEGIN
IF NOT EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and column_name = 'gps_speed')
THEN
alter table wvp_device_channel add gps_speed double precision;
END IF;
IF NOT EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and column_name = 'gps_altitude')
THEN
alter table wvp_device_channel add gps_altitude double precision;
END IF;
IF NOT EXISTS (SELECT column_name FROM information_schema.columns
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and column_name = 'gps_direction')
THEN
alter table wvp_device_channel add gps_direction double precision;
END IF;
END; //
call wvp_20250319();
DROP PROCEDURE wvp_20250319;
DELIMITER ;
/*
* 20250402
*/
DELIMITER // -- 重定义分隔符避免分号冲突
CREATE PROCEDURE `wvp_20250402`()
BEGIN
IF NOT EXISTS (SELECT column_name FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and INDEX_NAME = 'data_type')
THEN
create index data_type on wvp_device_channel (data_type);
END IF;
IF NOT EXISTS (SELECT column_name FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = (SELECT DATABASE()) and table_name = 'wvp_device_channel' and INDEX_NAME = 'data_device_id')
THEN
create index data_device_id on wvp_device_channel (data_device_id);
END IF;
END; //
call wvp_20250402();
DROP PROCEDURE wvp_20250402;
DELIMITER ;

19
docker/nginx/Dockerfile Normal file
View File

@ -0,0 +1,19 @@
FROM nginx:alpine
RUN apk add --no-cache bash
ARG TZ=Asia/Shanghai
RUN \
sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
apk update && \
apk add tzdata
RUN ln -sf /usr/share/zoneinfo/${TZ} /etc/localtime && \
echo '${TZ}' > /etc/timezone
RUN rm -rf /etc/nginx/conf.d/*
RUN mkdir /opt/dist
COPY ./dist /opt/dist
COPY ./conf/nginx.conf /etc/nginx/conf.d
CMD ["nginx","-g","daemon off;"]

11
docker/nginx/build.sh Executable file
View File

@ -0,0 +1,11 @@
#/bin/bash
set -e
version=2.7.3
rm ./dist/static/js/config.js
cp ./config.js ./dist/static/js/
docker build -t polaris-nginx:${version} .
docker tag polaris-nginx:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-nginx:${version}
docker tag polaris-nginx:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-nginx:latest

View File

@ -0,0 +1,55 @@
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 8080;
server_name localhost;
location / {
root /opt/dist;
index index.html index.htm;
}
location /record_proxy/{
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://polaris-wvp:18978/;
}
location /api/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://polaris-wvp:18978;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}

22
docker/nginx/config.js Normal file
View File

@ -0,0 +1,22 @@
window.baseUrl = "http://10.10.1.124:18978"
// map组件全局参数, 注释此内容可以关闭地图功能
window.mapParam = {
// 开启/关闭地图功能
enable: true,
// 坐标系 GCJ-02 WGS-84,
coordinateSystem: "GCJ-02",
// 地图瓦片地址
tilesUrl: "http://webrd0{1-4}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=8",
// 瓦片大小
tileSize: 256,
// 默认层级
zoom:10,
// 默认地图中心点
center:[116.41020, 39.915119],
// 地图最大层级
maxZoom:18,
// 地图最小层级
minZoom: 3
}

15
docker/push.sh Executable file
View File

@ -0,0 +1,15 @@
#/bin/bash
set -e
version=2.7.3
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-media:latest
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-mysql:latest
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-redis:latest
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-wvp:latest
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-nginx:latest
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-media:${version}
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-mysql:${version}
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-redis:${version}
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-wvp:${version}
docker push polaris-tian-docker.pkg.coding.net/qt/polaris/ylcx-nginx:${version}

5
docker/redis/Dockerfile Normal file
View File

@ -0,0 +1,5 @@
FROM redis
RUN mkdir -p /opt/polaris/redis
WORKDIR /opt/polaris/redis
COPY ./conf/redis.conf /opt/polaris/redis/redis.conf

8
docker/redis/build.sh Executable file
View File

@ -0,0 +1,8 @@
#/bin/bash
set -e
version=2.7.3
docker build -t polaris-redis:${version} .
docker tag polaris-redis:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-redis:${version}
docker tag polaris-redis:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-redis:latest

View File

@ -0,0 +1,2 @@
#requirepass root
bind 0.0.0.0

View File

@ -1,2 +0,0 @@
requirepass root
bind 0.0.0.0

View File

@ -1,81 +1,64 @@
FROM ubuntu:20.04 as build FROM ubuntu:20.04 AS build
ARG Platfrom=amd64
ARG JDK_NAME
ARG gitUrl="https://gitee.com/pan648540858" EXPOSE 18978/tcp
ARG zlmGitUrl="https://gitee.com/xia-chu/ZLMediaKit" EXPOSE 8116/tcp
EXPOSE 8116/udp
EXPOSE 8080/tcp
RUN export DEBIAN_FRONTEND=noninteractive &&\ RUN apt-get update && \
apt-get update && \ DEBIAN_FRONTEND="noninteractive" \
apt-get install -y --no-install-recommends openjdk-11-jre git maven nodejs npm build-essential \ apt-get install -y --no-install-recommends \
cmake ca-certificates openssl ffmpeg &&\ wget \
mkdir -p /opt/wvp/config /opt/wvp/heapdump /opt/wvp/config /opt/assist/config /opt/assist/heapdump /opt/media/www/record cmake \
maven \
RUN cd /home && \ git \
git clone "${gitUrl}/maven.git" && \ ca-certificates \
cp maven/settings.xml /usr/share/maven/conf/ tzdata \
curl \
RUN cd /home && \ libpcre3 \
git clone "${gitUrl}/wvp-GB28181-pro.git" libpcre3-dev \
RUN cd /home/wvp-GB28181-pro/web_src && \ zlib1g-dev \
npm install && \ openssl \
npm run build libssl-dev \
RUN cd /home/wvp-GB28181-pro && \ gdb && \
mvn clean package -Dmaven.test.skip=true && \
cp /home/wvp-GB28181-pro/target/*.jar /opt/wvp/ && \
cp /home/wvp-GB28181-pro/src/main/resources/application-docker.yml /opt/wvp/config/application.yml
RUN cd /home && \
git clone "${gitUrl}/wvp-pro-assist.git"
RUN cd /home/wvp-pro-assist && \
git reset --hard 58f1a79136a55a7cd1593c95b56ddadcc2225b61 && \
mvn clean package -Dmaven.test.skip=true && \
cp /home/wvp-pro-assist/target/*.jar /opt/assist/ && \
cp /home/wvp-pro-assist/src/main/resources/application-dev.yml /opt/assist/config/application.yml
RUN cd /home && \
git clone --depth=1 "${zlmGitUrl}"
RUN cd /home/ZLMediaKit && \
git submodule update --init --recursive && \
mkdir -p build release/linux/Release/ &&\
cd build && \
cmake -DCMAKE_BUILD_TYPE=Release .. && \
make -j4 && \
rm -rf ../release/linux/Release/config.ini && \
cp -r ../release/linux/Release/* /opt/media
RUN cd /opt/wvp && \
echo '#!/bin/bash' > run.sh && \
echo 'echo ${WVP_IP}' >> run.sh && \
echo 'echo ${WVP_CONFIG}' >> run.sh && \
echo 'cd /opt/assist' >> run.sh && \
echo 'nohup java ${ASSIST_JVM_CONFIG} -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/assist/heapdump/ -jar *.jar --spring.config.location=/opt/assist/config/application.yml --userSettings.record=/opt/media/www/record/ --media.record-assist-port=18081 ${ASSIST_CONFIG} &' >> run.sh && \
echo 'nohup /opt/media/MediaServer -d -m 3 &' >> run.sh && \
echo 'cd /opt/wvp' >> run.sh && \
echo 'java ${WVP_JVM_CONFIG} -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/opt/wvp/heapdump/ -jar *.jar --spring.config.location=/opt/wvp/config/application.yml --media.record-assist-port=18081 ${WVP_CONFIG}' >> run.sh && \
chmod +x run.sh
FROM ubuntu:20.04
EXPOSE 18080/tcp
EXPOSE 5060/tcp
EXPOSE 5060/udp
EXPOSE 6379/tcp
EXPOSE 18081/tcp
EXPOSE 80/tcp
EXPOSE 1935/tcp
EXPOSE 554/tcp
EXPOSE 554/udp
EXPOSE 30000-30500/tcp
EXPOSE 30000-30500/udp
ENV LC_ALL zh_CN.UTF-8
RUN export DEBIAN_FRONTEND=noninteractive &&\
apt-get update && \
apt-get install -y --no-install-recommends openjdk-11-jre ca-certificates ffmpeg language-pack-zh-hans && \
apt-get autoremove -y && \ apt-get autoremove -y && \
apt-get clean -y && \ apt-get clean -y && \
rm -rf /var/lib/apt/lists/*dic rm -rf /var/lib/apt/lists/*
COPY --from=build /opt /opt # install jdk1.8
RUN mkdir -p /opt/download
WORKDIR /opt/download
RUN if [ "$Platfrom" = "arm64" ]; \
then \
wget https://polaris-tian-generic.pkg.coding.net/qt/autopliot/jdk-8u411-linux-aarch64.tar.gz?version=latest --no-check-certificate -O jdk-8.tar.gz && \
tar -zxvf /opt/download/jdk-8.tar.gz -C /usr/local/ --transform 's/jdk1.8.0_411/java/' && \
rm /opt/download/jdk-8.tar.gz; \
else \
wget https://polaris-tian-generic.pkg.coding.net/qt/autopliot/jdk-8u202-linux-x64.tar.gz?version=latest --no-check-certificate -O jdk-8.tar.gz && \
tar -zxvf /opt/download/jdk-8.tar.gz -C /usr/local/ --transform 's/jdk1.8.0_202/java/' && \
rm /opt/download/jdk-8.tar.gz; \
fi
ENV JAVA_HOME /usr/local/java/
ENV JRE_HOME ${JAVA_HOME}/jre
ENV CLASSPATH .:${JAVA_HOME}/lib:${JRE_HOME}/lib
ENV PATH ${JAVA_HOME}/bin:$PATH
RUN java -version && javac -version
RUN mkdir -p /opt/wvp
WORKDIR /opt/wvp WORKDIR /opt/wvp
CMD ["sh", "run.sh"] COPY ./wvp /opt/wvp
WORKDIR /home
RUN cd /home && \
git clone https://gitee.com/pan648540858/wvp-GB28181-pro.git
RUN cd /home/wvp-GB28181-pro && \
mvn clean package -Dmaven.test.skip=true && \
cp /home/wvp-GB28181-pro/target/*.jar /opt/wvp/wvp.jar
WORKDIR /opt/wvp
ENTRYPOINT ["java", "-Xms512m", "-Xmx1024m", "-XX:+HeapDumpOnOutOfMemoryError", "-XX:HeapDumpPath=/opt/ylcx/", "-jar", "wvp.jar", "--spring.config.location=/opt/ylcx/wvp/application.yml"]

8
docker/wvp/build.sh Executable file
View File

@ -0,0 +1,8 @@
#/bin/bash
set -e
version=2.7.3
docker build -t polaris-wvp:${version} .
docker tag polaris-wvp:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-wvp:${version}
docker tag polaris-wvp:${version} polaris-tian-docker.pkg.coding.net/qt/polaris/polaris-wvp:latest

View File

@ -0,0 +1,105 @@
spring:
# 设置接口超时时间
mvc:
async:
request-timeout: 20000
thymeleaf:
cache: false
# [可选]上传文件大小限制
servlet:
multipart:
max-file-size: 10MB
max-request-size: 100MB
# REDIS数据库配置
redis:
# [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1
host: 127.0.0.1
# [必须修改] 端口号
port: 6379
# [可选] 数据库 DB
database: 1
# [可选] 访问密码,若你的redis服务器没有设置密码就不需要用密码去连接
password:
# [可选] 超时时间
timeout: 30000
# mysql数据源
datasource:
dynamic:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true
username: root
password: root
#[可选] 监听的HTTP端口, 网页和接口调用都是这个端口
server:
port: 18978
ssl:
# [可选] 是否开启HTTPS访问
enabled: false
# 作为28181服务器的配置
sip:
# [必须修改] 本机的IP
ip: 127.0.0.1
# [可选]
port: 8116
# [可选]
domain: 3402000000
# [可选]
id: 34020000002000000001
password:
alarm: true
# 默认服务器配置
media:
id: polaris
# [必须修改]内网IP
ip: 127.0.0.1
http-port: 6080
# [可选] 返回流地址时的ip置空使用 media.ip
stream-ip: 127.0.0.1
# [可选] wvp在国标信令中使用的ip此ip为摄像机可以访问到的ip 置空使用 media.ip
sdp-ip: 127.0.0.1
# [可选] Hook IP, 默认使用sip.ip
hook-ip: 127.0.0.1
# [可选] sslport
http-ssl-port: 4443
rtp-proxy-port: 10000
rtmp-port: 10935
rtmp-ssl-port: 41935
rtsp-port: 5540
rtsp-ssl-port: 45540
# [可选]
secret: su6TiedN2rVAmBbIDX0aa0QTiBJLBdcf
# 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分 点播超时建议使用多端口测试
rtp:
# [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输
enable: false
# [可选]
port-range: 30000,30500
# [可选]
send-port-range: 50502,50506
record-path: /opt/media/record
record-day: 7
record-assist-port: 0
user-settings:
auto-apply-play: true
play-timeout: 30000
wait-track: false
record-push-live: false
record-sip: false
stream-on-demand: true
interface-authentication: false
broadcast-for-platform: TCP-PASSIVE
push-stream-after-ack: true
send-to-platforms-when-id-lost: true
interface-authentication-excludes:
- /api/**
push-authority: false
allowed-origins:
- http://localhost:8080
- http://127.0.0.1:8080
- http://0.0.0.0:8080
logging:
config: classpath:logback-spring.xml

View File

@ -0,0 +1,106 @@
spring:
# 设置接口超时时间
mvc:
async:
request-timeout: 20000
thymeleaf:
cache: false
# [可选]上传文件大小限制
servlet:
multipart:
max-file-size: 10MB
max-request-size: 100MB
# REDIS数据库配置
redis:
# [必须修改] Redis服务器IP, REDIS安装在本机的,使用127.0.0.1
host: ${REDIS_HOST:127.0.0.1}
# [必须修改] 端口号
port: ${REDIS_PORT:6379}
# [可选] 数据库 DB
database: 1
# [可选] 访问密码,若你的redis服务器没有设置密码就不需要用密码去连接
password:
# [可选] 超时时间
timeout: 30000
# mysql数据源
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://${DATABASE_HOST:127.0.0.1}:${DATABASE_PORT:3306}/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true
username: ${DATABASE_USER:root}
password: ${DATABASE_PASSWORD:root}
#[可选] 监听的HTTP端口, 网页和接口调用都是这个端口
server:
port: 18978
ssl:
# [可选] 是否开启HTTPS访问
enabled: false
# 作为28181服务器的配置
sip:
# [必须修改] 本机的IP
ip: ${SIP_HOST:127.0.0.1}
# [可选]
port: 8116
# [可选]
domain: 3402000000
# [可选]
id: 34020000002000000001
password:
alarm: true
# 默认服务器配置
media:
id: polaris
# [必须修改] ZLM 内网IP与端口
ip: ${ZLM_HOST:127.0.0.1}
http-port: ${ZLM_PORT:6080}
# [可选] 返回流地址时的ip置空使用 media.ip
stream-ip: ${STREAM_HOST:127.0.0.1}
# [可选] wvp在国标信令中使用的ip此ip为摄像机可以访问到的ip 置空使用 media.ip
sdp-ip: ${SIP_HOST:127.0.0.1}
# [可选] Hook IP, 默认使用sip.ip
hook-ip: ${SIP_HOST:127.0.0.1}
# [可选] sslport
http-ssl-port: 4443
rtp-proxy-port: 10000
rtmp-port: 10935
rtmp-ssl-port: 41935
rtsp-port: 5540
rtsp-ssl-port: 45540
# [可选]
secret: ${ZLM_SERCERT}
# 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分 点播超时建议使用多端口测试
rtp:
# [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输
enable: false
# [可选]
port-range: 30000,30500
# [可选]
send-port-range: 50502,50506
record-path: /opt/media/record
record-day: 7
record-assist-port: 0
user-settings:
auto-apply-play: true
play-timeout: 30000
wait-track: false
record-push-live: false
record-sip: false
stream-on-demand: true
interface-authentication: false
broadcast-for-platform: TCP-PASSIVE
push-stream-after-ack: true
send-to-platforms-when-id-lost: true
interface-authentication-excludes:
- /api/**
push-authority: false
allowed-origins:
- http://localhost:8080
- http://127.0.0.1:8080
- http://0.0.0.0:8080
- ${NGINX_HOST}
logging:
config: classpath:logback-spring.xml

5
docker/wvp/wvp/application.yml Executable file
View File

@ -0,0 +1,5 @@
spring:
application:
name: wvp
profiles:
active: docker

17
pom.xml
View File

@ -187,7 +187,6 @@
<version>1.4.6</version> <version>1.4.6</version>
</dependency> </dependency>
<!--在线文档 -->
<!--在线文档 --> <!--在线文档 -->
<dependency> <dependency>
<groupId>org.springdoc</groupId> <groupId>org.springdoc</groupId>
@ -200,13 +199,6 @@
<version>1.6.10</version> <version>1.6.10</version>
</dependency> </dependency>
<!--在线文档 -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.10</version>
</dependency>
<dependency> <dependency>
<groupId>com.github.xiaoymin</groupId> <groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-springdoc-ui</artifactId> <artifactId>knife4j-springdoc-ui</artifactId>
@ -250,12 +242,17 @@
<dependency> <dependency>
<groupId>com.alibaba.fastjson2</groupId> <groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId> <artifactId>fastjson2</artifactId>
<version>2.0.17</version> <version>2.0.57</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.alibaba.fastjson2</groupId> <groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2-extension</artifactId> <artifactId>fastjson2-extension</artifactId>
<version>2.0.17</version> <version>2.0.57</version>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2-extension-spring5</artifactId>
<version>2.0.57</version>
</dependency> </dependency>
<!-- okhttp --> <!-- okhttp -->

122
run.sh Normal file
View File

@ -0,0 +1,122 @@
#!/bin/bash
# JDK路径
export JAVA_HOME=/usr/local/java/jdk1.8.0_202
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
NC='\033[0m'
function log() {
message="[Polaris Log]: $1 "
case "$1" in
*"失败"* | *"错误"* | *"请使用 root 或 sudo 权限运行此脚本"*)
echo -e "${RED}${message}${NC}" 2>&1 | tee -a
;;
*"成功"*)
echo -e "${GREEN}${message}${NC}" 2>&1 | tee -a
;;
*"忽略"* | *"跳过"*)
echo -e "${YELLOW}${message}${NC}" 2>&1 | tee -a
;;
*)
echo -e "${BLUE}${message}${NC}" 2>&1 | tee -a
;;
esac
}
echo
cat <<EOF
██████╗ ██████╗ ██╗ █████╗ ██████╗ ██╗███████╗
██╔══██╗██╔═══██╗██║ ██╔══██╗██╔══██╗██║██╔════╝
██████╔╝██║ ██║██║ ███████║██████╔╝██║███████╗
██╔═══╝ ██║ ██║██║ ██╔══██║██╔══██╗██║╚════██║
██║ ╚██████╔╝███████╗██║ ██║██║ ██║██║███████║
╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚══════╝
EOF
# WVP-pro defines
AppName=wvp-pro-2.7.18-11211115.jar
AppHome="/root/polaris/wvp/"
# JVM参数
JVM_OPTS="-Dname=$AppName -Duser.timezone=Asia/Shanghai -Xms512m -Xmx2048m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=1024m -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:NewRatio=1 -XX:SurvivorRatio=30 -XX:+UseParallelGC -XX:+UseParallelOldGC"
function start() {
log "======================= 开启流媒体服务 ======================="
log "AppHome: $AppHome"
cd $AppHome
log "AppName: $AppName"
PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'`
if [x"$PID" != x""]; then
log "$AppName is running..."
else
nohup java $JVM_OPTS -jar $AppName > /dev/null 2>&1 &
fi
log "流媒体服务开启成功"
}
function stop() {
log "======================= 停止流媒体服务 ======================="
PID=""
query() {
PID=$(ps -ef | grep java | grep $AppName | grep -v grep | awk '{print $2}')
}
query
if [ x"$PID" != x"" ]; then
log "进程PID: $PID"
kill -TERM $PID
log "$AppName (pid:$PID) exiting..."
while [ x"$PID" != x"" ]; do
sleep 1
query
done
log "成功:$AppName exited."
else
log "忽略:进程不存在"
fi
}
function status() {
log "======================= 运行状态 ======================="
log ""
PID=`ps -ef |grep java|grep $AppName|grep -v grep|awk '{print $2}'`
if [ $PID != 0 ]; then
log "进程PID: $PID"
log "$AppName is running..."
else
log "$AppName is not running..."
fi
log ""
log "========================================================"
}
function restart() {
stop
sleep 3
start
}
case $1 in
start)
start
;;
stop)
stop
;;
restart)
restart
;;
status)
status
;;
*) ;;
esac

View File

@ -1,5 +0,0 @@
package com.genersoft.iot.vmp.common;
public interface GeneralCallback<T>{
void run(int code, String msg, T data);
}

View File

@ -1,4 +1,4 @@
package com.genersoft.iot.vmp.gb28181.bean; package com.genersoft.iot.vmp.common;
public class RemoteAddressInfo { public class RemoteAddressInfo {
private String ip; private String ip;

View File

@ -0,0 +1,7 @@
package com.genersoft.iot.vmp.common;
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
public interface SubscribeCallback{
public void run(String deviceId, SipTransactionInfo transactionInfo);
}

View File

@ -22,10 +22,6 @@ public class VideoManagerConstants {
public static final String INVITE_PREFIX = "VMP_GB_INVITE_INFO"; public static final String INVITE_PREFIX = "VMP_GB_INVITE_INFO";
public static final String PLATFORM_CATCH_PREFIX = "VMP_PLATFORM_CATCH_";
public static final String PLATFORM_REGISTER_INFO_PREFIX = "VMP_PLATFORM_REGISTER_INFO_";
public static final String SEND_RTP_PORT = "VM_SEND_RTP_PORT:"; public static final String SEND_RTP_PORT = "VM_SEND_RTP_PORT:";
public static final String SEND_RTP_INFO_CALLID = "VMP_SEND_RTP_INFO:CALL_ID:"; public static final String SEND_RTP_INFO_CALLID = "VMP_SEND_RTP_INFO:CALL_ID:";
public static final String SEND_RTP_INFO_STREAM = "VMP_SEND_RTP_INFO:STREAM:"; public static final String SEND_RTP_INFO_STREAM = "VMP_SEND_RTP_INFO:STREAM:";

View File

@ -34,5 +34,5 @@ public class SipConfig {
private boolean alarm = false; private boolean alarm = false;
private long timeout = 150; private long timeout = 1000;
} }

View File

@ -1,71 +0,0 @@
package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.gb28181.bean.Platform;
import com.genersoft.iot.vmp.gb28181.bean.PlatformCatch;
import com.genersoft.iot.vmp.gb28181.service.IPlatformService;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 系统启动时控制上级平台重新注册
* @author lin
*/
@Slf4j
@Component
@Order(value=13)
public class SipPlatformRunner implements CommandLineRunner {
@Autowired
private IRedisCatchStorage redisCatchStorage;
@Autowired
private IPlatformService platformService;
@Autowired
private ISIPCommanderForPlatform sipCommanderForPlatform;
@Autowired
private UserSetting userSetting;
@Override
public void run(String... args) throws Exception {
// 获取所有启用的平台
List<Platform> parentPlatforms = platformService.queryEnablePlatformList(userSetting.getServerId());
for (Platform platform : parentPlatforms) {
PlatformCatch platformCatchOld = redisCatchStorage.queryPlatformCatchInfo(platform.getServerGBId());
// 更新缓存
PlatformCatch platformCatch = new PlatformCatch();
platformCatch.setPlatform(platform);
platformCatch.setId(platform.getServerGBId());
redisCatchStorage.updatePlatformCatchInfo(platformCatch);
if (platformCatchOld != null) {
// 取消订阅
try {
log.info("[平台主动注销] {}({})", platform.getName(), platform.getServerGBId());
sipCommanderForPlatform.unregister(platform, platformCatchOld.getSipTransactionInfo(), null, (eventResult)->{
platformService.login(platform);
});
} catch (Exception e) {
log.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
platformService.offline(platform, true);
continue;
}
}else {
platformService.login(platform);
}
// 设置平台离线
platformService.offline(platform, false);
}
}
}

View File

@ -190,5 +190,11 @@ public class UserSetting {
*/ */
private boolean autoRegisterPlatform = false; private boolean autoRegisterPlatform = false;
/**
* 按需发送推流设备位置 默认发送移动位置订阅时如果位置不变则不发送 设置为false按照国标间隔持续发送
*/
private boolean sendPositionOnDemand = true;
} }

View File

@ -38,7 +38,6 @@ public class RedisMsgListenConfig {
@Autowired @Autowired
private RedisCloseStreamMsgListener redisCloseStreamMsgListener; private RedisCloseStreamMsgListener redisCloseStreamMsgListener;
@Autowired @Autowired
private RedisRpcConfig redisRpcConfig; private RedisRpcConfig redisRpcConfig;

View File

@ -18,7 +18,6 @@ import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.sip.message.Response;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.HashMap; import java.util.HashMap;

View File

@ -1,18 +1,23 @@
package com.genersoft.iot.vmp.gb28181.bean; package com.genersoft.iot.vmp.gb28181.bean;
import lombok.Data;
/** /**
* 通过redis分发报警消息 * 通过redis分发报警消息
*/ */
@Data
public class AlarmChannelMessage { public class AlarmChannelMessage {
/** /**
* 国标编号 * 通道国标编号
*/ */
private String gbId; private String gbId;
/** /**
* 报警编号 * 报警编号
*/ */
private int alarmSn; private int alarmSn;
/** /**
* 告警类型 * 告警类型
*/ */
@ -22,36 +27,4 @@ public class AlarmChannelMessage {
* 报警描述 * 报警描述
*/ */
private String alarmDescription; private String alarmDescription;
public String getGbId() {
return gbId;
}
public void setGbId(String gbId) {
this.gbId = gbId;
}
public int getAlarmSn() {
return alarmSn;
}
public void setAlarmSn(int alarmSn) {
this.alarmSn = alarmSn;
}
public int getAlarmType() {
return alarmType;
}
public void setAlarmType(int alarmType) {
this.alarmType = alarmType;
}
public String getAlarmDescription() {
return alarmDescription;
}
public void setAlarmDescription(String alarmDescription) {
this.alarmDescription = alarmDescription;
}
} }

View File

@ -86,6 +86,18 @@ public class CommonGBChannel {
@Schema(description = "国标-纬度 WGS-84坐标系") @Schema(description = "国标-纬度 WGS-84坐标系")
private Double gbLatitude; private Double gbLatitude;
@Schema(description = "")
private Double gpsAltitude;
@Schema(description = "")
private Double gpsSpeed;
@Schema(description = "")
private Double gpsDirection;
@Schema(description = "")
private String gpsTime;
@Schema(description = "国标-虚拟组织所属的业务分组ID") @Schema(description = "国标-虚拟组织所属的业务分组ID")
private String gbBusinessGroupId; private String gbBusinessGroupId;

View File

@ -2,10 +2,12 @@ package com.genersoft.iot.vmp.gb28181.bean;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
/** /**
* 业务分组 * 业务分组
*/ */
@EqualsAndHashCode(callSuper = true)
@Data @Data
@Schema(description = "业务分组树") @Schema(description = "业务分组树")
public class GroupTree extends Group{ public class GroupTree extends Group{

View File

@ -0,0 +1,5 @@
package com.genersoft.iot.vmp.gb28181.bean;
public interface PlatformKeepaliveCallback {
public void run(String platformServerGbId, int failCount);
}

View File

@ -2,10 +2,12 @@ package com.genersoft.iot.vmp.gb28181.bean;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode;
/** /**
* 区域 * 区域
*/ */
@EqualsAndHashCode(callSuper = true)
@Data @Data
@Schema(description = "区域树") @Schema(description = "区域树")
public class RegionTree extends Region { public class RegionTree extends Region {

View File

@ -1,13 +1,17 @@
package com.genersoft.iot.vmp.gb28181.bean; package com.genersoft.iot.vmp.gb28181.bean;
import gov.nist.javax.sip.message.SIPResponse; import gov.nist.javax.sip.message.SIPResponse;
import lombok.Data;
@Data
public class SipTransactionInfo { public class SipTransactionInfo {
private String callId; private String callId;
private String fromTag; private String fromTag;
private String toTag; private String toTag;
private String viaBranch; private String viaBranch;
private int expires;
private String user;
// 自己是否媒体流发送者 // 自己是否媒体流发送者
private boolean asSender; private boolean asSender;
@ -31,43 +35,4 @@ public class SipTransactionInfo {
public SipTransactionInfo() { public SipTransactionInfo() {
} }
public String getCallId() {
return callId;
}
public void setCallId(String callId) {
this.callId = callId;
}
public String getFromTag() {
return fromTag;
}
public void setFromTag(String fromTag) {
this.fromTag = fromTag;
}
public String getToTag() {
return toTag;
}
public void setToTag(String toTag) {
this.toTag = toTag;
}
public String getViaBranch() {
return viaBranch;
}
public void setViaBranch(String viaBranch) {
this.viaBranch = viaBranch;
}
public boolean isAsSender() {
return asSender;
}
public void setAsSender(boolean asSender) {
this.asSender = asSender;
}
} }

View File

@ -1,19 +1,20 @@
package com.genersoft.iot.vmp.gb28181.bean; package com.genersoft.iot.vmp.gb28181.bean;
import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* @author lin * @author lin
*/ */
@Slf4j
@Component @Component
public class SubscribeHolder { public class SubscribeHolder {
@ -23,104 +24,97 @@ public class SubscribeHolder {
@Autowired @Autowired
private UserSetting userSetting; private UserSetting userSetting;
private final String taskOverduePrefix = "subscribe_overdue_"; @Autowired
private RedisTemplate<Object, Object> redisTemplate;
private static ConcurrentHashMap<String, SubscribeInfo> catalogMap = new ConcurrentHashMap<>();
private static ConcurrentHashMap<String, SubscribeInfo> mobilePositionMap = new ConcurrentHashMap<>();
private final String prefix = "VMP_SUBSCRIBE_OVERDUE";
public void putCatalogSubscribe(String platformId, SubscribeInfo subscribeInfo) { public void putCatalogSubscribe(String platformId, SubscribeInfo subscribeInfo) {
catalogMap.put(platformId, subscribeInfo); log.info("[国标级联] 添加目录订阅,平台: {} 有效期: {}", platformId, subscribeInfo.getExpires());
String key = String.format("%s_%s_%s_%s", prefix, userSetting.getServerId(), "catalog", platformId);
if (subscribeInfo.getExpires() > 0) { if (subscribeInfo.getExpires() > 0) {
// 添加订阅到期 Duration duration = Duration.ofSeconds(subscribeInfo.getExpires());
String taskOverdueKey = taskOverduePrefix + "catalog_" + platformId; redisTemplate.opsForValue().set(key, subscribeInfo, duration);
// 添加任务处理订阅过期 }else {
dynamicTask.startDelay(taskOverdueKey, () -> removeCatalogSubscribe(subscribeInfo.getId()), redisTemplate.opsForValue().set(key, subscribeInfo);
subscribeInfo.getExpires() * 1000);
} }
} }
public SubscribeInfo getCatalogSubscribe(String platformId) { public SubscribeInfo getCatalogSubscribe(String platformId) {
return catalogMap.get(platformId); String key = String.format("%s_%s_%s_%s", prefix, userSetting.getServerId(), "catalog", platformId);
return (SubscribeInfo)redisTemplate.opsForValue().get(key);
} }
public void removeCatalogSubscribe(String platformId) { public void removeCatalogSubscribe(String platformId) {
String key = String.format("%s_%s_%s_%s", prefix, userSetting.getServerId(), "catalog", platformId);
catalogMap.remove(platformId); redisTemplate.delete(key);
String taskOverdueKey = taskOverduePrefix + "catalog_" + platformId;
Runnable runnable = dynamicTask.get(taskOverdueKey);
if (runnable instanceof ISubscribeTask) {
ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
subscribeTask.stop(null);
}
// 添加任务处理订阅过期
dynamicTask.stop(taskOverdueKey);
} }
public void putMobilePositionSubscribe(String platformId, SubscribeInfo subscribeInfo, Runnable gpsTask) { public void putMobilePositionSubscribe(String platformId, SubscribeInfo subscribeInfo, Runnable gpsTask) {
mobilePositionMap.put(platformId, subscribeInfo); log.info("[国标级联] 添加移动位置订阅,平台: {} 有效期: {}s", platformId, subscribeInfo.getExpires());
String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + userSetting.getServerId() + "MobilePosition_" + platformId; String key = String.format("%s_%s_%s_%s", prefix, userSetting.getServerId(), "mobilePosition", platformId);
// 添加任务处理GPS定时推送 if (subscribeInfo.getExpires() > 0) {
Duration duration = Duration.ofSeconds(subscribeInfo.getExpires());
redisTemplate.opsForValue().set(key, subscribeInfo, duration);
}else {
redisTemplate.opsForValue().set(key, subscribeInfo);
}
int cycleForCatalog; int cycleForCatalog;
if (subscribeInfo.getGpsInterval() <= 0) { if (subscribeInfo.getGpsInterval() <= 0) {
cycleForCatalog = 5; cycleForCatalog = 5;
}else { }else {
cycleForCatalog = subscribeInfo.getGpsInterval(); cycleForCatalog = subscribeInfo.getGpsInterval();
} }
dynamicTask.startCron(key, gpsTask, dynamicTask.startCron(
cycleForCatalog * 1000); key,
String taskOverdueKey = taskOverduePrefix + "MobilePosition_" + platformId; () -> {
if (subscribeInfo.getExpires() > 0) { SubscribeInfo subscribe = getMobilePositionSubscribe(platformId);
// 添加任务处理订阅过期 if (subscribe != null) {
dynamicTask.startDelay(taskOverdueKey, () -> { gpsTask.run();
removeMobilePositionSubscribe(subscribeInfo.getId()); }else {
}, dynamicTask.stop(key);
subscribeInfo.getExpires() * 1000);
} }
},
cycleForCatalog * 1000);
} }
public SubscribeInfo getMobilePositionSubscribe(String platformId) { public SubscribeInfo getMobilePositionSubscribe(String platformId) {
return mobilePositionMap.get(platformId); String key = String.format("%s_%s_%s_%s", prefix, userSetting.getServerId(), "mobilePosition", platformId);
return (SubscribeInfo)redisTemplate.opsForValue().get(key);
} }
public void removeMobilePositionSubscribe(String platformId) { public void removeMobilePositionSubscribe(String platformId) {
mobilePositionMap.remove(platformId); String key = String.format("%s_%s_%s_%s", prefix, userSetting.getServerId(), "mobilePosition", platformId);
String key = VideoManagerConstants.SIP_SUBSCRIBE_PREFIX + userSetting.getServerId() + "MobilePosition_" + platformId; redisTemplate.delete(key);
// 结束任务处理GPS定时推送
dynamicTask.stop(key);
String taskOverdueKey = taskOverduePrefix + "MobilePosition_" + platformId;
Runnable runnable = dynamicTask.get(taskOverdueKey);
if (runnable instanceof ISubscribeTask) {
ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
subscribeTask.stop(null);
}
// 添加任务处理订阅过期
dynamicTask.stop(taskOverdueKey);
} }
public List<String> getAllCatalogSubscribePlatform() { public List<String> getAllCatalogSubscribePlatform(List<Platform> platformList) {
List<String> platforms = new ArrayList<>(); if (platformList == null || platformList.isEmpty()) {
if(catalogMap.size() > 0) { return new ArrayList<>();
for (String key : catalogMap.keySet()) { }
platforms.add(catalogMap.get(key).getId()); List<String> result = new ArrayList<>();
for (Platform platform : platformList) {
String key = String.format("%s_%s_%s_%s", prefix, userSetting.getServerId(), "catalog", platform.getServerGBId());
if (redisTemplate.hasKey(key)) {
result.add(platform.getServerGBId());
} }
} }
return platforms; return result;
} }
public List<String> getAllMobilePositionSubscribePlatform() { public List<String> getAllMobilePositionSubscribePlatform(List<Platform> platformList) {
List<String> platforms = new ArrayList<>(); if (platformList == null || platformList.isEmpty()) {
if(!mobilePositionMap.isEmpty()) { return new ArrayList<>();
for (String key : mobilePositionMap.keySet()) { }
platforms.add(mobilePositionMap.get(key).getId()); List<String> result = new ArrayList<>();
for (Platform platform : platformList) {
String key = String.format("%s_%s_%s_%s", prefix, userSetting.getServerId(), "mobilePosition", platform.getServerGBId());
if (redisTemplate.hasKey(key)) {
result.add(platform.getServerGBId());
} }
} }
return platforms; return result;
}
public void removeAllSubscribe(String platformId) {
removeMobilePositionSubscribe(platformId);
removeCatalogSubscribe(platformId);
} }
} }

View File

@ -1,36 +1,19 @@
package com.genersoft.iot.vmp.gb28181.bean; package com.genersoft.iot.vmp.gb28181.bean;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.message.SIPResponse; import gov.nist.javax.sip.message.SIPResponse;
import lombok.Data; import lombok.Data;
import javax.sip.header.*; import javax.sip.header.EventHeader;
import java.util.UUID; import java.util.UUID;
@Data @Data
public class SubscribeInfo { public class SubscribeInfo {
public SubscribeInfo(SIPRequest request, String id) {
this.id = id;
this.request = request;
this.expires = request.getExpires().getExpires();
EventHeader eventHeader = (EventHeader)request.getHeader(EventHeader.NAME);
this.eventId = eventHeader.getEventId();
this.eventType = eventHeader.getEventType();
}
public SubscribeInfo() {
}
private String id; private String id;
private SIPRequest request;
private int expires; private int expires;
private String eventId; private String eventId;
private String eventType; private String eventType;
private SIPResponse response; private SipTransactionInfo transactionInfo;
/** /**
* 以下为可选字段 * 以下为可选字段
@ -55,6 +38,16 @@ public class SubscribeInfo {
private String simulatedCallId; private String simulatedCallId;
public static SubscribeInfo getInstance(SIPResponse response, String id, int expires, EventHeader eventHeader){
SubscribeInfo subscribeInfo = new SubscribeInfo();
subscribeInfo.id = id;
subscribeInfo.transactionInfo = new SipTransactionInfo(response);
subscribeInfo.expires = expires;
subscribeInfo.eventId = eventHeader.getEventId();
subscribeInfo.eventType = eventHeader.getEventType();
return subscribeInfo;
}
public static SubscribeInfo buildSimulated(String platFormServerId, String platFormServerIp){ public static SubscribeInfo buildSimulated(String platFormServerId, String platFormServerIp){
SubscribeInfo subscribeInfo = new SubscribeInfo(); SubscribeInfo subscribeInfo = new SubscribeInfo();
subscribeInfo.setId(platFormServerId); subscribeInfo.setId(platFormServerId);

View File

@ -148,7 +148,6 @@ public class CommonChannelController {
@Parameter(name = "query", description = "查询内容") @Parameter(name = "query", description = "查询内容")
@Parameter(name = "online", description = "是否在线") @Parameter(name = "online", description = "是否在线")
@Parameter(name = "channelType", description = "通道类型, 0国标设备1推流设备2拉流代理") @Parameter(name = "channelType", description = "通道类型, 0国标设备1推流设备2拉流代理")
@Parameter(name = "civilCode", description = "行政区划")
@GetMapping("/civilCode/unusual/list") @GetMapping("/civilCode/unusual/list")
public PageInfo<CommonGBChannel> queryListByCivilCodeForUnusual(int page, int count, public PageInfo<CommonGBChannel> queryListByCivilCodeForUnusual(int page, int count,
@RequestParam(required = false) String query, @RequestParam(required = false) String query,
@ -160,14 +159,36 @@ public class CommonChannelController {
return channelService.queryListByCivilCodeForUnusual(page, count, query, online, channelType); return channelService.queryListByCivilCodeForUnusual(page, count, query, online, channelType);
} }
@Operation(summary = "清除存在行政区划但无法挂载的通道列表", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "param", description = "当前页", required = true) @Operation(summary = "存在父节点编号但无法挂载的通道列表", security = @SecurityRequirement(name = JwtUtils.HEADER))
@GetMapping("/civilCode/unusual/clear") @Parameter(name = "page", description = "当前页", required = true)
public void queryListByCivilCodeForUnusual(ChannelToRegionParam param){ @Parameter(name = "count", description = "每页查询数量", required = true)
@Parameter(name = "query", description = "查询内容")
@Parameter(name = "online", description = "是否在线")
@Parameter(name = "channelType", description = "通道类型, 0国标设备1推流设备2拉流代理")
@GetMapping("/parent/unusual/list")
public PageInfo<CommonGBChannel> queryListByParentForUnusual(int page, int count,
@RequestParam(required = false) String query,
@RequestParam(required = false) Boolean online,
@RequestParam(required = false) Integer channelType){
if (ObjectUtils.isEmpty(query)){ if (ObjectUtils.isEmpty(query)){
query = null; query = null;
} }
return channelService.queryListByCivilCodeForUnusual(page, count, query, online, channelType); return channelService.queryListByParentForUnusual(page, count, query, online, channelType);
}
@Operation(summary = "清除存在行政区划但无法挂载的通道列表", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "param", description = "清理参数, all为true清理所有异常数据。 否则按照传入的设备Id清理", required = true)
@PostMapping("/civilCode/unusual/clear")
public void clearChannelCivilCode(@RequestBody ChannelToRegionParam param){
channelService.clearChannelCivilCode(param.getAll(), param.getChannelIds());
}
@Operation(summary = "清除存在分组节点但无法挂载的通道列表", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "param", description = "清理参数, all为true清理所有异常数据。 否则按照传入的设备Id清理", required = true)
@PostMapping("/parent/unusual/clear")
public void clearChannelParent(@RequestBody ChannelToRegionParam param){
channelService.clearChannelParent(param.getAll(), param.getChannelIds());
} }
@Operation(summary = "获取关联业务分组通道列表", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Operation(summary = "获取关联业务分组通道列表", security = @SecurityRequirement(name = JwtUtils.HEADER))

View File

@ -11,9 +11,6 @@ import com.genersoft.iot.vmp.gb28181.bean.SyncStatus;
import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService;
import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.gb28181.service.IDeviceService;
import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService; import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService;
import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask;
import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService; import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService;
@ -40,9 +37,6 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@Tag(name = "国标设备查询", description = "国标设备查询") @Tag(name = "国标设备查询", description = "国标设备查询")
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
@ -144,28 +138,10 @@ public class DeviceQuery {
} }
// 清除redis记录 // 清除redis记录
boolean isSuccess = deviceService.delete(deviceId); deviceService.delete(deviceId);
if (isSuccess) {
inviteStreamService.clearInviteInfo(deviceId);
// 停止此设备的订阅更新
Set<String> allKeys = dynamicTask.getAllKeys();
for (String key : allKeys) {
if (key.startsWith(deviceId)) {
Runnable runnable = dynamicTask.get(key);
if (runnable instanceof ISubscribeTask) {
ISubscribeTask subscribeTask = (ISubscribeTask) runnable;
subscribeTask.stop(null);
}
dynamicTask.stop(key);
}
}
JSONObject json = new JSONObject(); JSONObject json = new JSONObject();
json.put("deviceId", deviceId); json.put("deviceId", deviceId);
return json.toString(); return json.toString();
} else {
log.warn("设备信息删除API调用失败");
throw new ControllerException(ErrorCode.ERROR100.getCode(), "设备信息删除API调用失败");
}
} }
@Operation(summary = "分页查询子目录通道", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Operation(summary = "分页查询子目录通道", security = @SecurityRequirement(name = JwtUtils.HEADER))
@ -355,28 +331,6 @@ public class DeviceQuery {
return wvpResult; return wvpResult;
} }
@GetMapping("/{deviceId}/subscribe_info")
@Operation(summary = "获取设备的订阅状态", security = @SecurityRequirement(name = JwtUtils.HEADER))
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
public WVPResult<Map<String, Integer>> getSubscribeInfo(@PathVariable String deviceId) {
Set<String> allKeys = dynamicTask.getAllKeys();
Map<String, Integer> dialogStateMap = new HashMap<>();
for (String key : allKeys) {
if (key.startsWith(deviceId)) {
ISubscribeTask subscribeTask = (ISubscribeTask)dynamicTask.get(key);
if (subscribeTask instanceof CatalogSubscribeTask) {
dialogStateMap.put("catalog", 1);
}else if (subscribeTask instanceof MobilePositionSubscribeTask) {
dialogStateMap.put("mobilePosition", 1);
}
}
}
WVPResult<Map<String, Integer>> wvpResult = new WVPResult<>();
wvpResult.setCode(0);
wvpResult.setData(dialogStateMap);
return wvpResult;
}
@GetMapping("/snap/{deviceId}/{channelId}") @GetMapping("/snap/{deviceId}/{channelId}")
@Operation(summary = "请求截图") @Operation(summary = "请求截图")
@Parameter(name = "deviceId", description = "设备国标编号", required = true) @Parameter(name = "deviceId", description = "设备国标编号", required = true)

View File

@ -7,7 +7,7 @@ import com.genersoft.iot.vmp.gb28181.bean.MobilePosition;
import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.gb28181.service.IDeviceService;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder; import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.service.IMobilePositionService; import com.genersoft.iot.vmp.service.IMobilePositionService;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
import com.github.pagehelper.util.StringUtil; import com.github.pagehelper.util.StringUtil;
@ -39,7 +39,7 @@ public class MobilePositionController {
private IMobilePositionService mobilePositionService; private IMobilePositionService mobilePositionService;
@Autowired @Autowired
private SIPCommander cmder; private ISIPCommander cmder;
@Autowired @Autowired
private DeferredResultHolder resultHolder; private DeferredResultHolder resultHolder;

View File

@ -124,4 +124,20 @@ public class RegionController {
public void sync(){ public void sync(){
regionService.syncFromChannel(); regionService.syncFromChannel();
} }
@Operation(summary = "根据行政区划编号从文件中查询层级和描述")
@ResponseBody
@GetMapping("/description")
public String getDescription(String civilCode){
return regionService.getDescription(civilCode);
}
@Operation(summary = "根据行政区划编号从文件中查询层级并添加")
@ResponseBody
@GetMapping("/addByCivilCode")
public void addByCivilCode(String civilCode){
regionService.addByCivilCode(civilCode);
}
} }

View File

@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.dao;
import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.dao.provider.ChannelProvider; import com.genersoft.iot.vmp.gb28181.dao.provider.ChannelProvider;
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import com.genersoft.iot.vmp.streamPush.bean.StreamPush; import com.genersoft.iot.vmp.streamPush.bean.StreamPush;
import org.apache.ibatis.annotations.*; import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@ -307,6 +308,14 @@ public interface CommonGBChannelMapper {
" </script>"}) " </script>"})
int removeCivilCodeByChannels(List<CommonGBChannel> channelList); int removeCivilCodeByChannels(List<CommonGBChannel> channelList);
@Update(value = {" <script>" +
" UPDATE wvp_device_channel " +
" SET gb_civil_code = null, civil_code = null" +
" WHERE id in "+
" <foreach collection='channelIdList' item='item' open='(' separator=',' close=')' > #{item}</foreach>" +
" </script>"})
int removeCivilCodeByChannelIds(List<Integer> channelIdList);
@SelectProvider(type = ChannelProvider.class, method = "queryByCivilCode") @SelectProvider(type = ChannelProvider.class, method = "queryByCivilCode")
List<CommonGBChannel> queryByCivilCode(@Param("civilCode") String civilCode); List<CommonGBChannel> queryByCivilCode(@Param("civilCode") String civilCode);
@ -443,13 +452,18 @@ public interface CommonGBChannelMapper {
List<CommonGBChannel> queryListByStreamPushList(@Param("dataType") Integer dataType, List<StreamPush> streamPushList); List<CommonGBChannel> queryListByStreamPushList(@Param("dataType") Integer dataType, List<StreamPush> streamPushList);
@Update(value = {" <script>" + @Update(value = {" <script>" +
" <foreach collection='channels' item='item' separator=';' >" + " <foreach collection='gpsMsgInfoList' item='item' separator=';' >" +
" UPDATE wvp_device_channel " + " UPDATE wvp_device_channel " +
" SET gb_longitude=#{item.gbLongitude}, gb_latitude=#{item.gbLatitude} " + " SET gb_longitude=#{item.lng}" +
" WHERE data_type = #{dataType} AND gb_device_id=#{item.gbDeviceId} "+ ", gb_latitude=#{item.lat} " +
", gps_speed=#{item.speed} " +
", gps_altitude=#{item.altitude} " +
", gps_direction=#{item.direction} " +
", gps_time=#{item.time} " +
" WHERE gb_device_id=#{item.id} "+
"</foreach>"+ "</foreach>"+
" </script>"}) " </script>"})
void updateGpsByDeviceIdForStreamPush(@Param("dataType") Integer dataType, List<CommonGBChannel> channels); void updateGpsByDeviceId(List<GPSMsgInfo> gpsMsgInfoList);
@SelectProvider(type = ChannelProvider.class, method = "queryList") @SelectProvider(type = ChannelProvider.class, method = "queryList")
List<CommonGBChannel> queryList(@Param("query") String query, @Param("online") Boolean online, List<CommonGBChannel> queryList(@Param("query") String query, @Param("online") Boolean online,
@ -548,4 +562,20 @@ public interface CommonGBChannelMapper {
@SelectProvider(type = ChannelProvider.class, method = "queryListByCivilCodeForUnusual") @SelectProvider(type = ChannelProvider.class, method = "queryListByCivilCodeForUnusual")
List<CommonGBChannel> queryListByCivilCodeForUnusual(@Param("query") String query, @Param("online") Boolean online, @Param("dataType")Integer dataType); List<CommonGBChannel> queryListByCivilCodeForUnusual(@Param("query") String query, @Param("online") Boolean online, @Param("dataType")Integer dataType);
@SelectProvider(type = ChannelProvider.class, method = "queryAllForUnusualCivilCode")
List<Integer> queryAllForUnusualCivilCode();
@SelectProvider(type = ChannelProvider.class, method = "queryListByParentForUnusual")
List<CommonGBChannel> queryListByParentForUnusual(@Param("query") String query, @Param("online") Boolean online, @Param("dataType")Integer dataType);
@SelectProvider(type = ChannelProvider.class, method = "queryAllForUnusualParent")
List<Integer> queryAllForUnusualParent();
@Update(value = {" <script>" +
" UPDATE wvp_device_channel " +
" SET gb_parent_id = null, gb_business_group_id = null, parent_id = null, business_group_id = null" +
" WHERE id in "+
" <foreach collection='channelIdsForClear' item='item' open='(' separator=',' close=')' > #{item}</foreach>" +
" </script>"})
void removeParentIdByChannelIds(List<Integer> channelIdsForClear);
} }

View File

@ -123,23 +123,27 @@ public interface DeviceMapper {
@Update(value = {" <script>" + @Update(value = {" <script>" +
"UPDATE wvp_device " + "UPDATE wvp_device " +
"SET update_time=#{updateTime}" + "SET update_time=#{updateTime}" +
"<if test=\"name != null\">, name=#{name}</if>" + ", name=#{name}" +
"<if test=\"manufacturer != null\">, manufacturer=#{manufacturer}</if>" + ", manufacturer=#{manufacturer}" +
"<if test=\"model != null\">, model=#{model}</if>" + ", model=#{model}" +
"<if test=\"firmware != null\">, firmware=#{firmware}</if>" + ", firmware=#{firmware}" +
"<if test=\"transport != null\">, transport=#{transport}</if>" + ", transport=#{transport}" +
"<if test=\"ip != null\">, ip=#{ip}</if>" + ", ip=#{ip}" +
"<if test=\"localIp != null\">, local_ip=#{localIp}</if>" + ", local_ip=#{localIp}" +
"<if test=\"port != null\">, port=#{port}</if>" + ", port=#{port}" +
"<if test=\"hostAddress != null\">, host_address=#{hostAddress}</if>" + ", host_address=#{hostAddress}" +
"<if test=\"onLine != null\">, on_line=#{onLine}</if>" + ", on_line=#{onLine}" +
"<if test=\"registerTime != null\">, register_time=#{registerTime}</if>" + ", register_time=#{registerTime}" +
"<if test=\"keepaliveTime != null\">, keepalive_time=#{keepaliveTime}</if>" + ", keepalive_time=#{keepaliveTime}" +
"<if test=\"heartBeatInterval != null\">, heart_beat_interval=#{heartBeatInterval}</if>" + ", heart_beat_interval=#{heartBeatInterval}" +
"<if test=\"positionCapability != null\">, position_capability=#{positionCapability}</if>" + ", position_capability=#{positionCapability}" +
"<if test=\"heartBeatCount != null\">, heart_beat_count=#{heartBeatCount}</if>" + ", heart_beat_count=#{heartBeatCount}" +
"<if test=\"expires != null\">, expires=#{expires}</if>" + ", subscribe_cycle_for_catalog=#{subscribeCycleForCatalog}" +
"<if test=\"serverId != null\">, server_id=#{serverId}</if>" + ", subscribe_cycle_for_mobile_position=#{subscribeCycleForMobilePosition}" +
", mobile_position_submission_interval=#{mobilePositionSubmissionInterval}" +
", subscribe_cycle_for_alarm=#{subscribeCycleForAlarm}" +
", expires=#{expires}" +
", server_id=#{serverId}" +
"WHERE device_id=#{deviceId}"+ "WHERE device_id=#{deviceId}"+
" </script>"}) " </script>"})
int update(Device device); int update(Device device);
@ -381,7 +385,7 @@ public interface DeviceMapper {
" OR device_id LIKE concat('%',#{query},'%') escape '/' " + " OR device_id LIKE concat('%',#{query},'%') escape '/' " +
" OR ip LIKE concat('%',#{query},'%') escape '/')" + " OR ip LIKE concat('%',#{query},'%') escape '/')" +
"</if> " + "</if> " +
" order by create_time desc "+ " order by create_time desc, device_id " +
" </script>") " </script>")
List<Device> getDeviceList(@Param("dataType") Integer dataType, @Param("query") String query, @Param("status") Boolean status); List<Device> getDeviceList(@Param("dataType") Integer dataType, @Param("query") String query, @Param("status") Boolean status);

View File

@ -262,6 +262,10 @@ public interface PlatformChannelMapper {
" wdc.data_device_id,\n" + " wdc.data_device_id,\n" +
" wdc.create_time,\n" + " wdc.create_time,\n" +
" wdc.update_time,\n" + " wdc.update_time,\n" +
" wdc.gps_altitude,\n" +
" wdc.gps_speed,\n" +
" wdc.gps_direction,\n" +
" wdc.gps_time,\n" +
" coalesce(wpgc.custom_device_id, wdc.gb_device_id, wdc.device_id) as gb_device_id,\n" + " coalesce(wpgc.custom_device_id, wdc.gb_device_id, wdc.device_id) as gb_device_id,\n" +
" coalesce(wpgc.custom_name, wdc.gb_name, wdc.name) as gb_name,\n" + " coalesce(wpgc.custom_name, wdc.gb_name, wdc.name) as gb_name,\n" +
" coalesce(wpgc.custom_manufacturer, wdc.gb_manufacturer, wdc.manufacturer) as gb_manufacturer,\n" + " coalesce(wpgc.custom_manufacturer, wdc.gb_manufacturer, wdc.manufacturer) as gb_manufacturer,\n" +

View File

@ -78,7 +78,7 @@ public interface PlatformMapper {
List<Platform> queryList(@Param("query") String query); List<Platform> queryList(@Param("query") String query);
@Select("SELECT * FROM wvp_platform WHERE server_id=#{serverId} and enable=#{enable} ") @Select("SELECT * FROM wvp_platform WHERE server_id=#{serverId} and enable=#{enable} ")
List<Platform> queryEnableParentPlatformList(@Param("serverId") String serverId, @Param("enable") boolean enable); List<Platform> queryEnableParentPlatformListByServerId(@Param("serverId") String serverId, @Param("enable") boolean enable);
@Select("SELECT * FROM wvp_platform WHERE enable=true and as_message_channel=true") @Select("SELECT * FROM wvp_platform WHERE enable=true and as_message_channel=true")
List<Platform> queryEnablePlatformListWithAsMessageChannel(); List<Platform> queryEnablePlatformListWithAsMessageChannel();
@ -89,12 +89,22 @@ public interface PlatformMapper {
@Select("SELECT * FROM wvp_platform WHERE id=#{id}") @Select("SELECT * FROM wvp_platform WHERE id=#{id}")
Platform query(int id); Platform query(int id);
@Update("UPDATE wvp_platform SET status=#{online} WHERE server_gb_id=#{platformGbID}" ) @Update("UPDATE wvp_platform SET status=#{online}, server_id = #{serverId} WHERE id=#{id}" )
int updateStatus(@Param("platformGbID") String platformGbID, @Param("online") boolean online); int updateStatus(@Param("id") int id, @Param("online") boolean online, @Param("serverId") String serverId);
@Select("SELECT server_id FROM wvp_platform WHERE enable=true and server_id != #{serverId} group by server_id") @Select("SELECT server_id FROM wvp_platform WHERE enable=true and server_id != #{serverId} group by server_id")
List<String> queryServerIdsWithEnableAndNotInServer(@Param("serverId") String serverId); List<String> queryServerIdsWithEnableAndNotInServer(@Param("serverId") String serverId);
@Select("SELECT * FROM wvp_platform WHERE server_id = #{serverId}") @Select("SELECT * FROM wvp_platform WHERE server_id = #{serverId}")
List<Platform> queryByServerId(@Param("serverId") String serverId); List<Platform> queryByServerId(@Param("serverId") String serverId);
@Select("SELECT * FROM wvp_platform ")
List<Platform> queryAll();
@Select("SELECT * FROM wvp_platform WHERE enable=true and server_id = #{serverId}")
List<Platform> queryServerIdsWithEnableAndServer(@Param("serverId") String serverId);
@Update("UPDATE wvp_platform SET status=false where server_id = #{serverId}" )
void offlineAll(@Param("serverId") String serverId);
} }

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.gb28181.dao; package com.genersoft.iot.vmp.gb28181.dao;
import com.genersoft.iot.vmp.common.CivilCodePo;
import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel;
import com.genersoft.iot.vmp.gb28181.bean.Region; import com.genersoft.iot.vmp.gb28181.bean.Region;
import com.genersoft.iot.vmp.gb28181.bean.RegionTree; import com.genersoft.iot.vmp.gb28181.bean.RegionTree;
@ -179,4 +180,12 @@ public interface RegionMapper {
" </script>") " </script>")
Set<Region> queryNotShareRegionForPlatformByRegionList(Set<Region> allRegion, @Param("platformId") Integer platformId); Set<Region> queryNotShareRegionForPlatformByRegionList(Set<Region> allRegion, @Param("platformId") Integer platformId);
@Select(" <script>" +
" SELECT device_id " +
" from wvp_common_region" +
" where device_id in " +
" <foreach collection='civilCodePoList' item='item' open='(' separator=',' close=')' > #{item.code}</foreach>" +
" </script>")
Set<String> queryInCivilCodePoList(List<CivilCodePo> civilCodePoList);
} }

View File

@ -431,4 +431,45 @@ public class ChannelProvider {
} }
return sqlBuild.toString(); return sqlBuild.toString();
} }
public String queryListByParentForUnusual(Map<String, Object> params ){
StringBuilder sqlBuild = new StringBuilder();
sqlBuild.append(BASE_SQL_TABLE_NAME);
sqlBuild.append(" left join (select wcg.device_id from wvp_common_group wcg) temp on temp.device_id = coalesce(wdc.gb_parent_id, wdc.parent_id)" +
" where coalesce(wdc.gb_parent_id, wdc.parent_id) is not null and temp.device_id is null ");
sqlBuild.append(" AND wdc.channel_type = 0 ");
if (params.get("query") != null) {
sqlBuild.append(" AND (coalesce(wdc.gb_device_id, wdc.device_id) LIKE concat('%',#{query},'%') escape '/'" +
" OR coalesce(wdc.gb_name, wdc.name) LIKE concat('%',#{query},'%') escape '/' )")
;
}
if (params.get("online") != null && (Boolean)params.get("online")) {
sqlBuild.append(" AND coalesce(wdc.gb_status, wdc.status) = 'ON'");
}
if (params.get("online") != null && !(Boolean)params.get("online")) {
sqlBuild.append(" AND coalesce(wdc.gb_status, wdc.status) = 'OFF'");
}
if (params.get("dataType") != null) {
sqlBuild.append(" AND wdc.data_type = #{dataType}");
}
return sqlBuild.toString();
}
public String queryAllForUnusualCivilCode(Map<String, Object> params ){
StringBuilder sqlBuild = new StringBuilder();
sqlBuild.append("select wdc.id from wvp_device_channel wdc ");
sqlBuild.append(" left join (select wcr.device_id from wvp_common_region wcr) temp on temp.device_id = coalesce(wdc.gb_civil_code, wdc.civil_code)" +
" where coalesce(wdc.gb_civil_code, wdc.civil_code) is not null and temp.device_id is null ");
sqlBuild.append(" AND wdc.channel_type = 0 ");
return sqlBuild.toString();
}
public String queryAllForUnusualParent(Map<String, Object> params ){
StringBuilder sqlBuild = new StringBuilder();
sqlBuild.append("select wdc.id from wvp_device_channel wdc ");
sqlBuild.append(" left join (select wcg.device_id from wvp_common_group wcg) temp on temp.device_id = coalesce(wdc.gb_parent_id, wdc.parent_id)" +
" where coalesce(wdc.gb_parent_id, wdc.parent_id) is not null and temp.device_id is null ");
sqlBuild.append(" AND wdc.channel_type = 0 ");
return sqlBuild.toString();
}
} }

View File

@ -101,6 +101,7 @@ public class DeviceChannelProvider {
} }
sqlBuild.append(" )"); sqlBuild.append(" )");
} }
sqlBuild.append("ORDER BY device_id");
return sqlBuild.toString(); return sqlBuild.toString();
} }

View File

@ -12,6 +12,7 @@ import com.genersoft.iot.vmp.media.bean.MediaServer;
import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOfflineEvent; import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOfflineEvent;
import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOnlineEvent; import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOnlineEvent;
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService; import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -26,6 +27,7 @@ import java.util.Set;
* @author: swwheihei * @author: swwheihei
* @date: 2020年5月6日 上午11:30:50 * @date: 2020年5月6日 上午11:30:50
*/ */
@Slf4j
@Component @Component
public class EventPublisher { public class EventPublisher {
@ -72,12 +74,7 @@ public class EventPublisher {
} }
public void catalogEventPublish(Platform platform, List<CommonGBChannel> deviceChannels, String type, boolean share) { public void catalogEventPublish(Platform platform, List<CommonGBChannel> deviceChannels, String type, boolean share) {
if (platform != null && !userSetting.getServerId().equals(platform.getServerId())) { if (platform != null && !userSetting.getServerId().equals(platform.getServerId())) {
// 指定了上级平台的推送则发送到指定的设备未指定的则全部发送 接收后各自处理自己的 log.info("[国标级联] 目录状态推送, 此上级平台由其他服务处理,消息已经忽略");
CatalogEvent outEvent = new CatalogEvent(this);
outEvent.setChannels(deviceChannels);
outEvent.setType(type);
outEvent.setPlatform(platform);
redisRpcService.catalogEventPublish(platform.getServerId(), outEvent);
return; return;
} }
CatalogEvent outEvent = new CatalogEvent(this); CatalogEvent outEvent = new CatalogEvent(this);
@ -96,12 +93,11 @@ public class EventPublisher {
} }
outEvent.setChannels(channels); outEvent.setChannels(channels);
outEvent.setType(type); outEvent.setType(type);
if (platform != null) {
outEvent.setPlatform(platform); outEvent.setPlatform(platform);
applicationEventPublisher.publishEvent(outEvent);
if (platform == null && share) {
// 如果没指定上级平台则推送消息到所有在线的wvp处理自己含有的平台的目录更新
redisRpcService.catalogEventPublish(null, outEvent);
} }
applicationEventPublisher.publishEvent(outEvent);
} }
public void mobilePositionEventPublish(MobilePosition mobilePosition) { public void mobilePositionEventPublish(MobilePosition mobilePosition) {

View File

@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.gb28181.event.sip; package com.genersoft.iot.vmp.gb28181.event.sip;
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import lombok.Data; import lombok.Data;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -27,6 +28,8 @@ public class SipEvent implements Delayed {
*/ */
private long delay; private long delay;
private SipTransactionInfo sipTransactionInfo;
public static SipEvent getInstance(String key, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent, long delay) { public static SipEvent getInstance(String key, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent, long delay) {
SipEvent sipEvent = new SipEvent(); SipEvent sipEvent = new SipEvent();
sipEvent.setKey(key); sipEvent.setKey(key);

View File

@ -8,7 +8,6 @@ import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo;
import com.genersoft.iot.vmp.gb28181.service.IPlatformChannelService; import com.genersoft.iot.vmp.gb28181.service.IPlatformChannelService;
import com.genersoft.iot.vmp.gb28181.service.IPlatformService; import com.genersoft.iot.vmp.gb28181.service.IPlatformService;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
@ -17,7 +16,10 @@ import org.springframework.stereotype.Component;
import javax.sip.InvalidArgumentException; import javax.sip.InvalidArgumentException;
import javax.sip.SipException; import javax.sip.SipException;
import java.text.ParseException; import java.text.ParseException;
import java.util.*; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** /**
* catalog事件 * catalog事件
@ -26,6 +28,9 @@ import java.util.*;
@Component @Component
public class CatalogEventLister implements ApplicationListener<CatalogEvent> { public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
@Autowired
private IPlatformService platformService;
@Autowired @Autowired
private IPlatformChannelService platformChannelService; private IPlatformChannelService platformChannelService;
@ -35,32 +40,45 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
@Autowired @Autowired
private SubscribeHolder subscribeHolder; private SubscribeHolder subscribeHolder;
@Autowired
private UserSetting userSetting;
@Override @Override
public void onApplicationEvent(CatalogEvent event) { public void onApplicationEvent(CatalogEvent event) {
SubscribeInfo subscribe = null; SubscribeInfo subscribe = null;
Platform parentPlatform = null; Platform parentPlatform = null;
log.info("[Catalog事件: {}] 通道数量: {}", event.getType(), event.getChannels().size());
Map<String, List<Platform>> parentPlatformMap = new HashMap<>(); Map<String, List<Platform>> platformMap = new HashMap<>();
Map<String, CommonGBChannel> channelMap = new HashMap<>(); Map<String, CommonGBChannel> channelMap = new HashMap<>();
if (event.getPlatform() != null) { if (event.getPlatform() != null) {
parentPlatform = event.getPlatform(); parentPlatform = event.getPlatform();
if (parentPlatform.getServerGBId() == null) {
log.info("[Catalog事件: {}] 平台服务国标编码未找到", event.getType());
return;
}
subscribe = subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId()); subscribe = subscribeHolder.getCatalogSubscribe(parentPlatform.getServerGBId());
if (subscribe == null) { if (subscribe == null) {
log.info("[Catalog事件: {}] 未订阅目录事件", event.getType());
return; return;
} }
}else { }else {
List<Platform> allPlatform = platformService.queryAll(userSetting.getServerId());
// 获取所用订阅 // 获取所用订阅
List<String> platforms = subscribeHolder.getAllCatalogSubscribePlatform(); List<String> platforms = subscribeHolder.getAllCatalogSubscribePlatform(allPlatform);
if (event.getChannels() != null) { if (event.getChannels() != null) {
if (!platforms.isEmpty()) { if (!platforms.isEmpty()) {
for (CommonGBChannel deviceChannel : event.getChannels()) { for (CommonGBChannel deviceChannel : event.getChannels()) {
List<Platform> parentPlatformsForGB = platformChannelService.queryPlatFormListByChannelDeviceId( List<Platform> parentPlatformsForGB = platformChannelService.queryPlatFormListByChannelDeviceId(
deviceChannel.getGbId(), platforms); deviceChannel.getGbId(), platforms);
parentPlatformMap.put(deviceChannel.getGbDeviceId(), parentPlatformsForGB); platformMap.put(deviceChannel.getGbDeviceId(), parentPlatformsForGB);
channelMap.put(deviceChannel.getGbDeviceId(), deviceChannel); channelMap.put(deviceChannel.getGbDeviceId(), deviceChannel);
} }
}else {
log.info("[Catalog事件: {}] 未订阅目录事件", event.getType());
} }
}else {
log.info("[Catalog事件: {}] 事件内通道数为0", event.getType());
} }
} }
switch (event.getType()) { switch (event.getType()) {
@ -69,32 +87,32 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
case CatalogEvent.DEL: case CatalogEvent.DEL:
if (parentPlatform != null) { if (parentPlatform != null) {
List<CommonGBChannel> deviceChannelList = new ArrayList<>(); List<CommonGBChannel> channels = new ArrayList<>();
if (event.getChannels() != null) { if (event.getChannels() != null) {
deviceChannelList.addAll(event.getChannels()); channels.addAll(event.getChannels());
} }
if (!deviceChannelList.isEmpty()) { if (!channels.isEmpty()) {
log.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), parentPlatform.getServerGBId(), deviceChannelList.size()); log.info("[Catalog事件: {}]平台:{},影响通道{}个", event.getType(), parentPlatform.getServerGBId(), channels.size());
try { try {
sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), parentPlatform, deviceChannelList, subscribe, null); sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), parentPlatform, channels, subscribe, null);
} catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException | } catch (InvalidArgumentException | ParseException | NoSuchFieldException | SipException |
IllegalAccessException e) { IllegalAccessException e) {
log.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); log.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage());
} }
} }
}else if (!parentPlatformMap.keySet().isEmpty()) { }else if (!platformMap.keySet().isEmpty()) {
for (String gbId : parentPlatformMap.keySet()) { for (String serverGbId : platformMap.keySet()) {
List<Platform> parentPlatforms = parentPlatformMap.get(gbId); List<Platform> platformList = platformMap.get(serverGbId);
if (parentPlatforms != null && !parentPlatforms.isEmpty()) { if (platformList != null && !platformList.isEmpty()) {
for (Platform platform : parentPlatforms) { for (Platform platform : platformList) {
SubscribeInfo subscribeInfo = subscribeHolder.getCatalogSubscribe(platform.getServerGBId()); SubscribeInfo subscribeInfo = subscribeHolder.getCatalogSubscribe(platform.getServerGBId());
if (subscribeInfo == null) { if (subscribeInfo == null) {
continue; continue;
} }
log.info("[Catalog事件: {}]平台:{},影响通道{}", event.getType(), platform.getServerGBId(), gbId); log.info("[Catalog事件: {}]平台:{},影响通道{}", event.getType(), platform.getServerGBId(), serverGbId);
List<CommonGBChannel> deviceChannelList = new ArrayList<>(); List<CommonGBChannel> deviceChannelList = new ArrayList<>();
CommonGBChannel deviceChannel = new CommonGBChannel(); CommonGBChannel deviceChannel = new CommonGBChannel();
deviceChannel.setGbDeviceId(gbId); deviceChannel.setGbDeviceId(serverGbId);
deviceChannelList.add(deviceChannel); deviceChannelList.add(deviceChannel);
try { try {
sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), platform, deviceChannelList, subscribeInfo, null); sipCommanderFroPlatform.sendNotifyForCatalogOther(event.getType(), platform, deviceChannelList, subscribeInfo, null);
@ -103,6 +121,8 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
log.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); log.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage());
} }
} }
}else {
log.info("[Catalog事件: {}] 未找到上级平台: {}", event.getType(), serverGbId);
} }
} }
} }
@ -127,9 +147,9 @@ public class CatalogEventLister implements ApplicationListener<CatalogEvent> {
log.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage()); log.error("[命令发送失败] 国标级联 Catalog通知: {}", e.getMessage());
} }
} }
}else if (!parentPlatformMap.keySet().isEmpty()) { }else if (!platformMap.keySet().isEmpty()) {
for (String gbId : parentPlatformMap.keySet()) { for (String gbId : platformMap.keySet()) {
List<Platform> parentPlatforms = parentPlatformMap.get(gbId); List<Platform> parentPlatforms = platformMap.get(gbId);
if (parentPlatforms != null && !parentPlatforms.isEmpty()) { if (parentPlatforms != null && !parentPlatforms.isEmpty()) {
for (Platform platform : parentPlatforms) { for (Platform platform : parentPlatforms) {
SubscribeInfo subscribeInfo = subscribeHolder.getCatalogSubscribe(platform.getServerGBId()); SubscribeInfo subscribeInfo = subscribeHolder.getCatalogSubscribe(platform.getServerGBId());

View File

@ -1,10 +1,12 @@
package com.genersoft.iot.vmp.gb28181.event.subscribe.mobilePosition; package com.genersoft.iot.vmp.gb28181.event.subscribe.mobilePosition;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel;
import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.bean.Platform;
import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder; import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo; import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo;
import com.genersoft.iot.vmp.gb28181.service.IPlatformChannelService; import com.genersoft.iot.vmp.gb28181.service.IPlatformChannelService;
import com.genersoft.iot.vmp.gb28181.service.IPlatformService;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderForPlatform; import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderForPlatform;
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -24,6 +26,9 @@ import java.util.List;
@Component @Component
public class MobilePositionEventLister implements ApplicationListener<MobilePositionEvent> { public class MobilePositionEventLister implements ApplicationListener<MobilePositionEvent> {
@Autowired
private IPlatformService platformService;
@Autowired @Autowired
private IPlatformChannelService platformChannelService; private IPlatformChannelService platformChannelService;
@ -33,14 +38,17 @@ public class MobilePositionEventLister implements ApplicationListener<MobilePosi
@Autowired @Autowired
private SubscribeHolder subscribeHolder; private SubscribeHolder subscribeHolder;
@Autowired
private UserSetting userSetting;
@Override @Override
public void onApplicationEvent(MobilePositionEvent event) { public void onApplicationEvent(MobilePositionEvent event) {
if (event.getMobilePosition().getChannelId() == 0) { if (event.getMobilePosition().getChannelId() == 0) {
return; return;
} }
List<Platform> allPlatforms = platformService.queryAll(userSetting.getServerId());
// 获取所用订阅 // 获取所用订阅
List<String> platforms = subscribeHolder.getAllMobilePositionSubscribePlatform(); List<String> platforms = subscribeHolder.getAllMobilePositionSubscribePlatform(allPlatforms);
if (platforms.isEmpty()) { if (platforms.isEmpty()) {
return; return;
} }
@ -65,4 +73,3 @@ public class MobilePositionEventLister implements ApplicationListener<MobilePosi
} }
} }
} }

View File

@ -32,7 +32,7 @@ public interface IDeviceService {
* @param device 设备信息 * @param device 设备信息
* @return 布尔 * @return 布尔
*/ */
boolean addCatalogSubscribe(Device device); boolean addCatalogSubscribe(Device device, SipTransactionInfo transactionInfo);
/** /**
* 移除目录订阅 * 移除目录订阅
@ -46,7 +46,7 @@ public interface IDeviceService {
* @param device 设备信息 * @param device 设备信息
* @return 布尔 * @return 布尔
*/ */
boolean addMobilePositionSubscribe(Device device); boolean addMobilePositionSubscribe(Device device, SipTransactionInfo transactionInfo);
/** /**
* 移除移动位置订阅 * 移除移动位置订阅

View File

@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.service;
import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.service.bean.ErrorCallback; import com.genersoft.iot.vmp.service.bean.ErrorCallback;
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import com.genersoft.iot.vmp.streamPush.bean.StreamPush; import com.genersoft.iot.vmp.streamPush.bean.StreamPush;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
@ -84,11 +85,18 @@ public interface IGbChannelService {
List<CommonGBChannel> queryListByStreamPushList(List<StreamPush> streamPushList); List<CommonGBChannel> queryListByStreamPushList(List<StreamPush> streamPushList);
void updateGpsByDeviceIdForStreamPush(List<CommonGBChannel> channels);
PageInfo<CommonGBChannel> queryList(int page, int count, String query, Boolean online, Boolean hasRecordPlan, Integer channelType); PageInfo<CommonGBChannel> queryList(int page, int count, String query, Boolean online, Boolean hasRecordPlan, Integer channelType);
void queryRecordInfo(CommonGBChannel channel, String startTime, String endTime, ErrorCallback<RecordInfo> callback); void queryRecordInfo(CommonGBChannel channel, String startTime, String endTime, ErrorCallback<RecordInfo> callback);
PageInfo<CommonGBChannel> queryListByCivilCodeForUnusual(int page, int count, String query, Boolean online, Integer channelType); PageInfo<CommonGBChannel> queryListByCivilCodeForUnusual(int page, int count, String query, Boolean online, Integer channelType);
void clearChannelCivilCode(Boolean all, List<Integer> channelIds);
PageInfo<CommonGBChannel> queryListByParentForUnusual(int page, int count, String query, Boolean online, Integer channelType);
void clearChannelParent(Boolean all, List<Integer> channelIds);
void updateGPSFromGPSMsgInfo(List<GPSMsgInfo> gpsMsgInfoList);
} }

View File

@ -48,4 +48,6 @@ public interface IPlatformChannelService {
void checkRegionAdd(List<CommonGBChannel> channelList); void checkRegionAdd(List<CommonGBChannel> channelList);
void checkRegionRemove(List<CommonGBChannel> channelList, List<Region> regionList); void checkRegionRemove(List<CommonGBChannel> channelList, List<Region> regionList);
List<Platform> queryByPlatformBySharChannelId(String gbId);
} }

View File

@ -53,13 +53,7 @@ public interface IPlatformService {
* 平台离线 * 平台离线
* @param parentPlatform 平台信息 * @param parentPlatform 平台信息
*/ */
void offline(Platform parentPlatform, boolean stopRegisterTask); void offline(Platform parentPlatform);
/**
* 向上级平台发起注册
* @param parentPlatform
*/
void login(Platform parentPlatform);
/** /**
* 向上级平台发送位置订阅 * 向上级平台发送位置订阅
@ -85,4 +79,7 @@ public interface IPlatformService {
List<Platform> queryEnablePlatformList(String serverId); List<Platform> queryEnablePlatformList(String serverId);
void delete(Integer platformId, CommonCallback<Object> callback); void delete(Integer platformId, CommonCallback<Object> callback);
List<Platform> queryAll(String serverId);
} }

View File

@ -36,4 +36,8 @@ public interface IRegionService {
boolean batchAdd(List<Region> regionList); boolean batchAdd(List<Region> regionList);
List<Region> getPath(String deviceId); List<Region> getPath(String deviceId);
String getDescription(String civilCode);
void addByCivilCode(String civilCode);
} }

View File

@ -15,9 +15,11 @@ import com.genersoft.iot.vmp.gb28181.service.IDeviceService;
import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService; import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService;
import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
import com.genersoft.iot.vmp.gb28181.session.SipInviteSessionManager; import com.genersoft.iot.vmp.gb28181.session.SipInviteSessionManager;
import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask; import com.genersoft.iot.vmp.gb28181.task.deviceSubscribe.SubscribeTask;
import com.genersoft.iot.vmp.gb28181.task.impl.CatalogSubscribeTask; import com.genersoft.iot.vmp.gb28181.task.deviceSubscribe.SubscribeTaskInfo;
import com.genersoft.iot.vmp.gb28181.task.impl.MobilePositionSubscribeTask; import com.genersoft.iot.vmp.gb28181.task.deviceSubscribe.SubscribeTaskRunner;
import com.genersoft.iot.vmp.gb28181.task.deviceSubscribe.impl.SubscribeTaskForCatalog;
import com.genersoft.iot.vmp.gb28181.task.deviceSubscribe.impl.SubscribeTaskForMobilPosition;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd.CatalogResponseMessageHandler;
import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.bean.MediaServer;
@ -32,13 +34,18 @@ import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo;
import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import gov.nist.javax.sip.message.SIPResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import javax.sip.InvalidArgumentException; import javax.sip.InvalidArgumentException;
import javax.sip.ResponseEvent;
import javax.sip.SipException; import javax.sip.SipException;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import java.text.ParseException; import java.text.ParseException;
@ -53,7 +60,8 @@ import java.util.concurrent.TimeUnit;
*/ */
@Slf4j @Slf4j
@Service @Service
public class DeviceServiceImpl implements IDeviceService { @Order(value=16)
public class DeviceServiceImpl implements IDeviceService, CommandLineRunner {
@Autowired @Autowired
private DynamicTask dynamicTask; private DynamicTask dynamicTask;
@ -100,10 +108,46 @@ public class DeviceServiceImpl implements IDeviceService {
@Autowired @Autowired
private IRedisRpcService redisRpcService; private IRedisRpcService redisRpcService;
@Autowired
private SubscribeTaskRunner subscribeTaskRunner;
private Device getDeviceByDeviceIdFromDb(String deviceId) { private Device getDeviceByDeviceIdFromDb(String deviceId) {
return deviceMapper.getDeviceByDeviceId(deviceId); return deviceMapper.getDeviceByDeviceId(deviceId);
} }
@Override
public void run(String... args) throws Exception {
// TODO 处理设备离线
// 处理订阅任务
List<SubscribeTaskInfo> taskInfoList = subscribeTaskRunner.getAllTaskInfo();
if (!taskInfoList.isEmpty()) {
for (SubscribeTaskInfo taskInfo : taskInfoList) {
if (taskInfo == null) {
continue;
}
Device device = getDeviceByDeviceId(taskInfo.getDeviceId());
if (device == null || !device.isOnLine()) {
subscribeTaskRunner.removeSubscribe(taskInfo.getKey());
continue;
}
if (SubscribeTaskForCatalog.name.equals(taskInfo.getName())) {
device.setSubscribeCycleForCatalog((int)taskInfo.getExpireTime());
SubscribeTask subscribeTask = SubscribeTaskForCatalog.getInstance(device, this::catalogSubscribeExpire, taskInfo.getTransactionInfo());
if (subscribeTask != null) {
subscribeTaskRunner.addSubscribe(subscribeTask);
}
}else if (SubscribeTaskForMobilPosition.name.equals(taskInfo.getName())) {
device.setSubscribeCycleForMobilePosition((int)taskInfo.getExpireTime());
SubscribeTask subscribeTask = SubscribeTaskForMobilPosition.getInstance(device, this::mobilPositionSubscribeExpire, taskInfo.getTransactionInfo());
if (subscribeTask != null) {
subscribeTaskRunner.addSubscribe(subscribeTask);
}
}
}
}
}
@Override @Override
public void online(Device device, SipTransactionInfo sipTransactionInfo) { public void online(Device device, SipTransactionInfo sipTransactionInfo) {
log.info("[设备上线] deviceId{}->{}:{}", device.getDeviceId(), device.getIp(), device.getPort()); log.info("[设备上线] deviceId{}->{}:{}", device.getDeviceId(), device.getIp(), device.getPort());
@ -148,6 +192,7 @@ public class DeviceServiceImpl implements IDeviceService {
} }
sync(device); sync(device);
}else { }else {
device.setServerId(userSetting.getServerId());
if(!device.isOnLine()){ if(!device.isOnLine()){
device.setOnLine(true); device.setOnLine(true);
device.setCreateTime(now); device.setCreateTime(now);
@ -164,12 +209,12 @@ public class DeviceServiceImpl implements IDeviceService {
// TODO 如果设备下的通道级联到了其他平台那么需要发送事件或者notify给上级平台 // TODO 如果设备下的通道级联到了其他平台那么需要发送事件或者notify给上级平台
} }
// 上线添加订阅 // 上线添加订阅
if (device.getSubscribeCycleForCatalog() > 0) { if (device.getSubscribeCycleForCatalog() > 0 && !subscribeTaskRunner.containsKey(SubscribeTaskForCatalog.getKey(device))) {
// 查询在线设备那些开启了订阅为设备开启定时的目录订阅 // 查询在线设备那些开启了订阅为设备开启定时的目录订阅
addCatalogSubscribe(device); addCatalogSubscribe(device, null);
} }
if (device.getSubscribeCycleForMobilePosition() > 0) { if (device.getSubscribeCycleForMobilePosition() > 0 && !subscribeTaskRunner.containsKey(SubscribeTaskForMobilPosition.getKey(device))) {
addMobilePositionSubscribe(device); addMobilePositionSubscribe(device, null);
} }
if (userSetting.getDeviceStatusNotify()) { if (userSetting.getDeviceStatusNotify()) {
// 发送redis消息 // 发送redis消息
@ -254,98 +299,173 @@ public class DeviceServiceImpl implements IDeviceService {
} }
} }
// 订阅丢失检查
@Scheduled(fixedDelay = 10, timeUnit = TimeUnit.SECONDS)
public void lostCheck(){
// 获取所有设备
List<Device> deviceList = redisCatchStorage.getAllDevices();
if (deviceList.isEmpty()) {
return;
}
for (Device device : deviceList) {
if (device == null || !device.isOnLine() || !device.getServerId().equals(userSetting.getServerId())) {
continue;
}
if (device.getSubscribeCycleForCatalog() > 0 && !subscribeTaskRunner.containsKey(SubscribeTaskForCatalog.getKey(device))) {
log.debug("[订阅丢失] 目录订阅, 编号: {}, 重新发起订阅", device.getDeviceId());
addCatalogSubscribe(device, null);
}
if (device.getSubscribeCycleForMobilePosition() > 0 && !subscribeTaskRunner.containsKey(SubscribeTaskForMobilPosition.getKey(device))) {
log.debug("[订阅丢失] 移动位置订阅, 编号: {}, 重新发起订阅", device.getDeviceId());
addMobilePositionSubscribe(device, null);
}
}
}
private void catalogSubscribeExpire(String deviceId, SipTransactionInfo transactionInfo) {
log.info("[目录订阅] 到期, 编号: {}", deviceId);
Device device = getDeviceByDeviceId(deviceId);
if (device == null) {
log.info("[目录订阅] 到期, 编号: {}, 设备不存在, 忽略", deviceId);
return;
}
if (device.getSubscribeCycleForCatalog() > 0) {
addCatalogSubscribe(device, transactionInfo);
}
}
private void mobilPositionSubscribeExpire(String deviceId, SipTransactionInfo transactionInfo) {
log.info("[移动位置订阅] 到期, 编号: {}", deviceId);
Device device = getDeviceByDeviceId(deviceId);
if (device == null) {
log.info("[移动位置订阅] 到期, 编号: {}, 设备不存在, 忽略", deviceId);
return;
}
if (device.getSubscribeCycleForMobilePosition() > 0) {
addMobilePositionSubscribe(device, transactionInfo);
}
}
@Override @Override
public boolean addCatalogSubscribe(Device device) { public boolean addCatalogSubscribe(@NotNull Device device, SipTransactionInfo transactionInfo) {
if (device == null || device.getSubscribeCycleForCatalog() < 0) { if (device == null || device.getSubscribeCycleForCatalog() < 0) {
return false; return false;
} }
log.info("[添加目录订阅] 设备 {}", device.getDeviceId()); log.info("[添加目录订阅] 设备 {}", device.getDeviceId());
// 添加目录订阅 try {
CatalogSubscribeTask catalogSubscribeTask = new CatalogSubscribeTask(device, sipCommander, dynamicTask); sipCommander.catalogSubscribe(device, transactionInfo, eventResult -> {
// 刷新订阅 ResponseEvent event = (ResponseEvent) eventResult.event;
int subscribeCycleForCatalog = Math.max(device.getSubscribeCycleForCatalog(),30); // 成功
// 设置最小值为30 log.info("[目录订阅]成功: {}", device.getDeviceId());
dynamicTask.startCron(device.getDeviceId() + "catalog", catalogSubscribeTask, (subscribeCycleForCatalog -1) * 1000); if (!subscribeTaskRunner.containsKey(SubscribeTaskForCatalog.getKey(device))) {
SIPResponse response = (SIPResponse) event.getResponse();
SipTransactionInfo transactionInfoForResonse = new SipTransactionInfo(response);
SubscribeTask subscribeTask = SubscribeTaskForCatalog.getInstance(device, this::catalogSubscribeExpire, transactionInfoForResonse);
if (subscribeTask != null) {
subscribeTaskRunner.addSubscribe(subscribeTask);
}
}else {
subscribeTaskRunner.updateDelay(SubscribeTaskForCatalog.getKey(device), (device.getSubscribeCycleForCatalog() * 1000L - 500L) + System.currentTimeMillis());
}
catalogSubscribeTask.run(); },eventResult -> {
// 失败
log.warn("[目录订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg);
});
} catch (InvalidArgumentException | SipException | ParseException e) {
log.error("[命令发送失败] 目录订阅: {}", e.getMessage());
return false;
}
return true; return true;
} }
@Override @Override
public boolean removeCatalogSubscribe(Device device, CommonCallback<Boolean> callback) { public boolean removeCatalogSubscribe(@NotNull Device device, CommonCallback<Boolean> callback) {
if (device == null || device.getSubscribeCycleForCatalog() < 0) {
if (callback != null) {
callback.run(false);
}
return false;
}
log.info("[移除目录订阅]: {}", device.getDeviceId()); log.info("[移除目录订阅]: {}", device.getDeviceId());
String taskKey = device.getDeviceId() + "catalog"; String key = SubscribeTaskForCatalog.getKey(device);
if (device.isOnLine()) { if (subscribeTaskRunner.containsKey(key)) {
Runnable runnable = dynamicTask.get(taskKey); SipTransactionInfo transactionInfo = subscribeTaskRunner.getTransactionInfo(key);
if (runnable instanceof ISubscribeTask) { if (transactionInfo == null) {
ISubscribeTask subscribeTask = (ISubscribeTask) runnable; log.warn("[移除目录订阅] 未找到事务信息,{}", device.getDeviceId());
subscribeTask.stop(callback); }
}else { try {
log.info("[移除目录订阅]失败,未找到订阅任务 : {}", device.getDeviceId()); device.setSubscribeCycleForCatalog(0);
sipCommander.catalogSubscribe(device, transactionInfo, eventResult -> {
// 成功
log.info("[取消目录订阅]成功: {}", device.getDeviceId());
subscribeTaskRunner.removeSubscribe(SubscribeTaskForCatalog.getKey(device));
if (callback != null) { if (callback != null) {
callback.run(false); callback.run(true);
}
},eventResult -> {
// 失败
log.warn("[取消目录订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg);
});
}catch (Exception e) {
// 失败
log.warn("[取消目录订阅]失败: {}-{} ", device.getDeviceId(), e.getMessage());
} }
} }
}else {
log.info("[移除移动位置订阅]失败,设备已经离线 : {}", device.getDeviceId());
if (callback != null) {
callback.run(false);
}
}
dynamicTask.stop(taskKey);
return true; return true;
} }
@Override @Override
public boolean addMobilePositionSubscribe(Device device) { public boolean addMobilePositionSubscribe(@NotNull Device device, SipTransactionInfo transactionInfo) {
if (device == null || device.getSubscribeCycleForMobilePosition() < 0) { log.info("[添加移动位置订阅] 设备 {}", device.getDeviceId());
try {
sipCommander.mobilePositionSubscribe(device, transactionInfo, eventResult -> {
ResponseEvent event = (ResponseEvent) eventResult.event;
// 成功
log.info("[移动位置订阅]成功: {}", device.getDeviceId());
if (!subscribeTaskRunner.containsKey(SubscribeTaskForMobilPosition.getKey(device))) {
SIPResponse response = (SIPResponse) event.getResponse();
SipTransactionInfo transactionInfoForResonse = new SipTransactionInfo(response);
SubscribeTask subscribeTask = SubscribeTaskForMobilPosition.getInstance(device, this::catalogSubscribeExpire, transactionInfoForResonse);
if (subscribeTask != null) {
subscribeTaskRunner.addSubscribe(subscribeTask);
}
}else {
subscribeTaskRunner.updateDelay(SubscribeTaskForMobilPosition.getKey(device), (device.getSubscribeCycleForCatalog() * 1000L - 500L) + System.currentTimeMillis());
}
},eventResult -> {
// 失败
log.warn("[移动位置订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg);
});
} catch (InvalidArgumentException | SipException | ParseException e) {
log.error("[命令发送失败] 移动位置订阅: {}", e.getMessage());
return false; return false;
} }
log.info("[添加移动位置订阅] 设备{}", device.getDeviceId());
// 添加目录订阅
MobilePositionSubscribeTask mobilePositionSubscribeTask = new MobilePositionSubscribeTask(device, sipCommander, dynamicTask);
// 设置最小值为30
int subscribeCycleForCatalog = Math.max(device.getSubscribeCycleForMobilePosition(),30);
// 刷新订阅
dynamicTask.startCron(device.getDeviceId() + "mobile_position" , mobilePositionSubscribeTask, subscribeCycleForCatalog * 1000);
mobilePositionSubscribeTask.run();
return true; return true;
} }
@Override @Override
public boolean removeMobilePositionSubscribe(Device device, CommonCallback<Boolean> callback) { public boolean removeMobilePositionSubscribe(Device device, CommonCallback<Boolean> callback) {
if (device == null || device.getSubscribeCycleForCatalog() < 0) {
if (callback != null) {
callback.run(false);
}
return false;
}
log.info("[移除移动位置订阅]: {}", device.getDeviceId()); log.info("[移除移动位置订阅]: {}", device.getDeviceId());
String taskKey = device.getDeviceId() + "mobile_position"; String key = SubscribeTaskForMobilPosition.getKey(device);
if (device.isOnLine()) { if (subscribeTaskRunner.containsKey(key)) {
Runnable runnable = dynamicTask.get(taskKey); SipTransactionInfo transactionInfo = subscribeTaskRunner.getTransactionInfo(key);
if (runnable instanceof ISubscribeTask) { if (transactionInfo == null) {
ISubscribeTask subscribeTask = (ISubscribeTask) runnable; log.warn("[移除移动位置订阅] 未找到事务信息,{}", device.getDeviceId());
subscribeTask.stop(callback); }
}else { try {
log.info("[移除移动位置订阅]失败,未找到订阅任务 : {}", device.getDeviceId()); device.setSubscribeCycleForMobilePosition(0);
sipCommander.mobilePositionSubscribe(device, transactionInfo, eventResult -> {
// 成功
log.info("[取消移动位置订阅]成功: {}", device.getDeviceId());
subscribeTaskRunner.removeSubscribe(SubscribeTaskForMobilPosition.getKey(device));
if (callback != null) { if (callback != null) {
callback.run(false); callback.run(true);
}
},eventResult -> {
// 失败
log.warn("[取消移动位置订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg);
});
}catch (Exception e) {
// 失败
log.warn("[取消移动位置订阅]失败: {}-{} ", device.getDeviceId(), e.getMessage());
} }
} }
}else {
log.info("[移除移动位置订阅]失败,设备已经离线 : {}", device.getDeviceId());
if (callback != null) {
callback.run(false);
}
}
dynamicTask.stop(taskKey);
return true; return true;
} }
@ -499,10 +619,20 @@ public class DeviceServiceImpl implements IDeviceService {
public boolean delete(String deviceId) { public boolean delete(String deviceId) {
Device device = getDeviceByDeviceIdFromDb(deviceId); Device device = getDeviceByDeviceIdFromDb(deviceId);
Assert.notNull(device, "未找到设备"); Assert.notNull(device, "未找到设备");
if (subscribeTaskRunner.containsKey(SubscribeTaskForCatalog.getKey(device))) {
removeCatalogSubscribe(device, null);
}
if (subscribeTaskRunner.containsKey(SubscribeTaskForMobilPosition.getKey(device))) {
removeMobilePositionSubscribe(device, null);
}
// 停止状态检测
String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId();
dynamicTask.stop(registerExpireTaskKey);
platformChannelMapper.delChannelForDeviceId(deviceId); platformChannelMapper.delChannelForDeviceId(deviceId);
deviceChannelMapper.cleanChannelsByDeviceId(device.getId()); deviceChannelMapper.cleanChannelsByDeviceId(device.getId());
deviceMapper.del(deviceId); deviceMapper.del(deviceId);
redisCatchStorage.removeDevice(deviceId); redisCatchStorage.removeDevice(deviceId);
inviteStreamService.clearInviteInfo(deviceId);
return true; return true;
} }
@ -562,20 +692,17 @@ public class DeviceServiceImpl implements IDeviceService {
// 订阅周期不同则先取消 // 订阅周期不同则先取消
removeCatalogSubscribe(device, result->{ removeCatalogSubscribe(device, result->{
device.setSubscribeCycleForCatalog(cycle); device.setSubscribeCycleForCatalog(cycle);
updateDevice(device);
if (cycle > 0) { if (cycle > 0) {
// 开启订阅 // 开启订阅
addCatalogSubscribe(device); addCatalogSubscribe(device, null);
} }
// 因为是异步执行需要在这里更新下数据
deviceMapper.updateSubscribeCatalog(device);
redisCatchStorage.updateDevice(device);
}); });
}else { }else {
// 开启订阅 // 开启订阅
device.setSubscribeCycleForCatalog(cycle); device.setSubscribeCycleForCatalog(cycle);
addCatalogSubscribe(device); updateDevice(device);
deviceMapper.updateSubscribeCatalog(device); addCatalogSubscribe(device, null);
redisCatchStorage.updateDevice(device);
} }
} }
@ -583,6 +710,7 @@ public class DeviceServiceImpl implements IDeviceService {
public void subscribeMobilePosition(int id, int cycle, int interval) { public void subscribeMobilePosition(int id, int cycle, int interval) {
Device device = deviceMapper.query(id); Device device = deviceMapper.query(id);
Assert.notNull(device, "未找到设备"); Assert.notNull(device, "未找到设备");
if (device.getSubscribeCycleForMobilePosition() == cycle) { if (device.getSubscribeCycleForMobilePosition() == cycle) {
return; return;
} }
@ -598,21 +726,16 @@ public class DeviceServiceImpl implements IDeviceService {
device.setSubscribeCycleForMobilePosition(cycle); device.setSubscribeCycleForMobilePosition(cycle);
device.setMobilePositionSubmissionInterval(interval); device.setMobilePositionSubmissionInterval(interval);
if (cycle > 0) { if (cycle > 0) {
addMobilePositionSubscribe(device); addMobilePositionSubscribe(device, null);
} }
// 因为是异步执行需要在这里更新下数据
deviceMapper.updateSubscribeMobilePosition(device);
redisCatchStorage.updateDevice(device);
}); });
}else { }else {
// 订阅未开启 // 订阅未开启
device.setSubscribeCycleForMobilePosition(cycle); device.setSubscribeCycleForMobilePosition(cycle);
device.setMobilePositionSubmissionInterval(interval); device.setMobilePositionSubmissionInterval(interval);
updateDevice(device);
// 开启订阅 // 开启订阅
addMobilePositionSubscribe(device); addMobilePositionSubscribe(device, null);
// 因为是异步执行需要在这里更新下数据
deviceMapper.updateSubscribeMobilePosition(device);
redisCatchStorage.updateDevice(device);
} }
} }

View File

@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.service.impl;
import com.genersoft.iot.vmp.common.InviteSessionType; import com.genersoft.iot.vmp.common.InviteSessionType;
import com.genersoft.iot.vmp.common.StreamInfo; import com.genersoft.iot.vmp.common.StreamInfo;
import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.conf.exception.ServiceException; import com.genersoft.iot.vmp.conf.exception.ServiceException;
import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.common.enums.ChannelDataType; import com.genersoft.iot.vmp.common.enums.ChannelDataType;
@ -145,6 +146,9 @@ public class GbChannelPlayServiceImpl implements IGbChannelPlayService {
deviceChannelPlayService.play(channel, record, callback); deviceChannelPlayService.play(channel, record, callback);
} catch (PlayException e) { } catch (PlayException e) {
callback.run(e.getCode(), e.getMsg(), null); callback.run(e.getCode(), e.getMsg(), null);
} catch (ControllerException e) {
log.error("[点播失败] {}({}), {}", channel.getGbName(), channel.getGbDeviceId(), e.getMsg());
callback.run(Response.BUSY_HERE, "busy here", null);
} catch (Exception e) { } catch (Exception e) {
log.error("[点播失败] {}({})", channel.getGbName(), channel.getGbDeviceId(), e); log.error("[点播失败] {}({})", channel.getGbName(), channel.getGbDeviceId(), e);
callback.run(Response.BUSY_HERE, "busy here", null); callback.run(Response.BUSY_HERE, "busy here", null);

View File

@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService;
import com.genersoft.iot.vmp.gb28181.service.IGbChannelService; import com.genersoft.iot.vmp.gb28181.service.IGbChannelService;
import com.genersoft.iot.vmp.gb28181.service.IPlatformChannelService; import com.genersoft.iot.vmp.gb28181.service.IPlatformChannelService;
import com.genersoft.iot.vmp.service.bean.ErrorCallback; import com.genersoft.iot.vmp.service.bean.ErrorCallback;
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import com.genersoft.iot.vmp.streamPush.bean.StreamPush; import com.genersoft.iot.vmp.streamPush.bean.StreamPush;
import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.utils.DateUtil;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
@ -159,30 +160,26 @@ public class GbChannelServiceImpl implements IGbChannelService {
log.warn("[多个通道离线] 通道数量为0更新失败"); log.warn("[多个通道离线] 通道数量为0更新失败");
return 0; return 0;
} }
List<CommonGBChannel> onlineChannelList = commonGBChannelMapper.queryInListByStatus(commonGBChannelList, "ON"); log.info("[通道离线] 共 {} 个", commonGBChannelList.size());
if (onlineChannelList.isEmpty()) {
log.info("[多个通道离线] 更新失败, 参数内通道已经离线, 无需更新");
return 0;
}
int limitCount = 1000; int limitCount = 1000;
int result = 0; int result = 0;
if (onlineChannelList.size() > limitCount) { if (commonGBChannelList.size() > limitCount) {
for (int i = 0; i < onlineChannelList.size(); i += limitCount) { for (int i = 0; i < commonGBChannelList.size(); i += limitCount) {
int toIndex = i + limitCount; int toIndex = i + limitCount;
if (i + limitCount > onlineChannelList.size()) { if (i + limitCount > commonGBChannelList.size()) {
toIndex = onlineChannelList.size(); toIndex = commonGBChannelList.size();
} }
result += commonGBChannelMapper.updateStatusForListById(onlineChannelList.subList(i, toIndex), "OFF"); result += commonGBChannelMapper.updateStatusForListById(commonGBChannelList.subList(i, toIndex), "OFF");
} }
} else { } else {
result += commonGBChannelMapper.updateStatusForListById(onlineChannelList, "OFF"); result += commonGBChannelMapper.updateStatusForListById(commonGBChannelList, "OFF");
} }
if (result > 0) { if (result > 0) {
try { try {
// 发送catalog // 发送catalog
eventPublisher.catalogEventPublish(null, onlineChannelList, CatalogEvent.OFF); eventPublisher.catalogEventPublish(null, commonGBChannelList, CatalogEvent.OFF);
} catch (Exception e) { } catch (Exception e) {
log.warn("[多个通道离线] 发送失败,数量:{}", onlineChannelList.size(), e); log.warn("[多个通道离线] 发送失败,数量:{}", commonGBChannelList.size(), e);
} }
} }
return result; return result;
@ -213,32 +210,25 @@ public class GbChannelServiceImpl implements IGbChannelService {
log.warn("[多个通道上线] 通道数量为0更新失败"); log.warn("[多个通道上线] 通道数量为0更新失败");
return 0; return 0;
} }
List<CommonGBChannel> offlineChannelList = commonGBChannelMapper.queryInListByStatus(commonGBChannelList, "OFF");
if (offlineChannelList.isEmpty()) {
log.warn("[多个通道上线] 更新失败, 参数内通道已经上线");
return 0;
}
// 批量更新 // 批量更新
int limitCount = 1000; int limitCount = 1000;
int result = 0; int result = 0;
if (offlineChannelList.size() > limitCount) { if (commonGBChannelList.size() > limitCount) {
for (int i = 0; i < offlineChannelList.size(); i += limitCount) { for (int i = 0; i < commonGBChannelList.size(); i += limitCount) {
int toIndex = i + limitCount; int toIndex = i + limitCount;
if (i + limitCount > offlineChannelList.size()) { if (i + limitCount > commonGBChannelList.size()) {
toIndex = offlineChannelList.size(); toIndex = commonGBChannelList.size();
} }
result += commonGBChannelMapper.updateStatusForListById(offlineChannelList.subList(i, toIndex), "ON"); result += commonGBChannelMapper.updateStatusForListById(commonGBChannelList.subList(i, toIndex), "ON");
} }
} else { } else {
result += commonGBChannelMapper.updateStatusForListById(offlineChannelList, "ON"); result += commonGBChannelMapper.updateStatusForListById(commonGBChannelList, "ON");
} }
if (result > 0) {
try { try {
// 发送catalog // 发送catalog
eventPublisher.catalogEventPublish(null, offlineChannelList, CatalogEvent.ON); eventPublisher.catalogEventPublish(null, commonGBChannelList, CatalogEvent.ON);
} catch (Exception e) { } catch (Exception e) {
log.warn("[多个通道上线] 发送失败,数量:{}", offlineChannelList.size(), e); log.warn("[多个通道上线] 发送失败,数量:{}", commonGBChannelList.size(), e);
}
} }
return result; return result;
@ -715,11 +705,6 @@ public class GbChannelServiceImpl implements IGbChannelService {
return commonGBChannelMapper.queryListByStreamPushList(ChannelDataType.STREAM_PUSH.value, streamPushList); return commonGBChannelMapper.queryListByStreamPushList(ChannelDataType.STREAM_PUSH.value, streamPushList);
} }
@Override
public void updateGpsByDeviceIdForStreamPush(List<CommonGBChannel> channels) {
commonGBChannelMapper.updateGpsByDeviceIdForStreamPush(ChannelDataType.STREAM_PUSH.value, channels);
}
@Override @Override
public PageInfo<CommonGBChannel> queryList(int page, int count, String query, Boolean online, Boolean hasRecordPlan, Integer channelType) { public PageInfo<CommonGBChannel> queryList(int page, int count, String query, Boolean online, Boolean hasRecordPlan, Integer channelType) {
PageHelper.startPage(page, count); PageHelper.startPage(page, count);
@ -762,4 +747,47 @@ public class GbChannelServiceImpl implements IGbChannelService {
List<CommonGBChannel> all = commonGBChannelMapper.queryListByCivilCodeForUnusual(query, online, channelType); List<CommonGBChannel> all = commonGBChannelMapper.queryListByCivilCodeForUnusual(query, online, channelType);
return new PageInfo<>(all); return new PageInfo<>(all);
} }
@Override
public void clearChannelCivilCode(Boolean all, List<Integer> channelIds) {
List<Integer> channelIdsForClear;
if (all != null && all) {
channelIdsForClear = commonGBChannelMapper.queryAllForUnusualCivilCode();
}else {
channelIdsForClear = channelIds;
}
commonGBChannelMapper.removeCivilCodeByChannelIds(channelIdsForClear);
}
@Override
public PageInfo<CommonGBChannel> queryListByParentForUnusual(int page, int count, String query, Boolean online, Integer channelType) {
PageHelper.startPage(page, count);
if (query != null) {
query = query.replaceAll("/", "//")
.replaceAll("%", "/%")
.replaceAll("_", "/_");
}
List<CommonGBChannel> all = commonGBChannelMapper.queryListByParentForUnusual(query, online, channelType);
return new PageInfo<>(all);
}
@Override
public void clearChannelParent(Boolean all, List<Integer> channelIds) {
List<Integer> channelIdsForClear;
if (all != null && all) {
channelIdsForClear = commonGBChannelMapper.queryAllForUnusualParent();
}else {
channelIdsForClear = channelIds;
}
commonGBChannelMapper.removeParentIdByChannelIds(channelIdsForClear);
}
@Override
public void updateGPSFromGPSMsgInfo(List<GPSMsgInfo> gpsMsgInfoList) {
if (gpsMsgInfoList == null || gpsMsgInfoList.isEmpty()) {
return;
}
commonGBChannelMapper.updateGpsByDeviceId(gpsMsgInfoList);
}
} }

View File

@ -601,4 +601,12 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService {
public List<CommonGBChannel> queryChannelByPlatformIdAndChannelIds(Integer platformId, List<Integer> channelIds) { public List<CommonGBChannel> queryChannelByPlatformIdAndChannelIds(Integer platformId, List<Integer> channelIds) {
return platformChannelMapper.queryShare(platformId, channelIds); return platformChannelMapper.queryShare(platformId, channelIds);
} }
@Override
public List<Platform> queryByPlatformBySharChannelId(String channelDeviceId) {
CommonGBChannel commonGBChannel = commonGBChannelMapper.queryByDeviceId(channelDeviceId);
ArrayList<Integer> ids = new ArrayList<>();
ids.add(commonGBChannel.getGbId());
return platformChannelMapper.queryPlatFormListByChannelList(ids);
}
} }

View File

@ -1,6 +1,5 @@
package com.genersoft.iot.vmp.gb28181.service.impl; package com.genersoft.iot.vmp.gb28181.service.impl;
import com.genersoft.iot.vmp.common.InviteInfo;
import com.genersoft.iot.vmp.common.*; import com.genersoft.iot.vmp.common.*;
import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.conf.SipConfig;
@ -15,6 +14,10 @@ import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService;
import com.genersoft.iot.vmp.gb28181.service.IPlatformService; import com.genersoft.iot.vmp.gb28181.service.IPlatformService;
import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; import com.genersoft.iot.vmp.gb28181.session.SSRCFactory;
import com.genersoft.iot.vmp.gb28181.session.SipInviteSessionManager; import com.genersoft.iot.vmp.gb28181.session.SipInviteSessionManager;
import com.genersoft.iot.vmp.gb28181.task.platformStatus.PlatformKeepaliveTask;
import com.genersoft.iot.vmp.gb28181.task.platformStatus.PlatformRegisterTask;
import com.genersoft.iot.vmp.gb28181.task.platformStatus.PlatformRegisterTaskInfo;
import com.genersoft.iot.vmp.gb28181.task.platformStatus.PlatformStatusTaskRunner;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.gb28181.utils.SipUtils; import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import com.genersoft.iot.vmp.media.bean.MediaInfo; import com.genersoft.iot.vmp.media.bean.MediaInfo;
@ -28,13 +31,14 @@ import com.genersoft.iot.vmp.service.ISendRtpServerService;
import com.genersoft.iot.vmp.service.bean.*; import com.genersoft.iot.vmp.service.bean.*;
import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService; import com.genersoft.iot.vmp.service.redisMsg.IRedisRpcService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.utils.DateUtil;
import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import gov.nist.javax.sip.message.SIPResponse; import gov.nist.javax.sip.message.SIPResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.event.EventListener; import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -56,12 +60,8 @@ import java.util.concurrent.TimeUnit;
*/ */
@Slf4j @Slf4j
@Service @Service
public class PlatformServiceImpl implements IPlatformService { @Order(value=15)
public class PlatformServiceImpl implements IPlatformService, CommandLineRunner {
private final static String REGISTER_KEY_PREFIX = "platform_register_";
private final static String REGISTER_FAIL_AGAIN_KEY_PREFIX = "platform_register_fail_again_";
private final static String KEEPALIVE_KEY_PREFIX = "platform_keepalive_";
@Autowired @Autowired
private PlatformMapper platformMapper; private PlatformMapper platformMapper;
@ -69,7 +69,6 @@ public class PlatformServiceImpl implements IPlatformService {
@Autowired @Autowired
private IRedisCatchStorage redisCatchStorage; private IRedisCatchStorage redisCatchStorage;
@Autowired @Autowired
private SSRCFactory ssrcFactory; private SSRCFactory ssrcFactory;
@ -109,6 +108,74 @@ public class PlatformServiceImpl implements IPlatformService {
@Autowired @Autowired
private ISendRtpServerService sendRtpServerService; private ISendRtpServerService sendRtpServerService;
@Autowired
private PlatformStatusTaskRunner statusTaskRunner;
@Override
public void run(String... args) throws Exception {
// 启动时 如果存在未过期的注册平台则发送注销
List<PlatformRegisterTaskInfo> registerTaskInfoList = statusTaskRunner.getAllRegisterTaskInfo();
if (registerTaskInfoList.isEmpty()) {
return;
}
for (PlatformRegisterTaskInfo taskInfo : registerTaskInfoList) {
log.info("[国标级联] 启动服务是发现平台注册仍在有效期,注销: {}", taskInfo.getPlatformServerId());
Platform platform = queryPlatformByServerGBId(taskInfo.getPlatformServerId());
if (platform == null) {
statusTaskRunner.removeRegisterTask(taskInfo.getPlatformServerId());
continue;
}
sendUnRegister(platform, taskInfo.getSipTransactionInfo());
}
// 启动时所有平台默认离线
platformMapper.offlineAll(userSetting.getServerId());
}
@Scheduled(fixedDelay = 20, timeUnit = TimeUnit.SECONDS) //每3秒执行一次
public void statusLostCheck(){
// 每隔20秒检测是否存在启用但是未注册的平台存在则发起注册
// 获取所有在线并且启用的平台
List<Platform> platformList = platformMapper.queryServerIdsWithEnableAndServer(userSetting.getServerId());
if (platformList.isEmpty()) {
return;
}
for (Platform platform : platformList) {
if (statusTaskRunner.containsRegister(platform.getServerGBId()) && statusTaskRunner.containsKeepAlive(platform.getServerGBId())) {
continue;
}
if (statusTaskRunner.containsRegister(platform.getServerGBId())) {
SipTransactionInfo transactionInfo = statusTaskRunner.getRegisterTransactionInfo(platform.getServerGBId());
// 注销后出发平台离线 如果是启用的平台那么下次丢失检测会检测到并重新注册上线
sendUnRegister(platform, transactionInfo);
}else {
statusTaskRunner.removeKeepAliveTask(platform.getServerGBId());
sendRegister(platform, null);
}
}
}
private void sendRegister(Platform platform, SipTransactionInfo sipTransactionInfo) {
try {
commanderForPlatform.register(platform, sipTransactionInfo, eventResult -> {
log.info("[国标级联] {}{},注册失败", platform.getName(), platform.getServerGBId());
offline(platform);
}, null);
} catch (InvalidArgumentException | ParseException | SipException e) {
log.error("[命令发送失败] 国标级联: {}", e.getMessage());
}
}
private void sendUnRegister(Platform platform, SipTransactionInfo sipTransactionInfo) {
statusTaskRunner.removeRegisterTask(platform.getServerGBId());
statusTaskRunner.removeKeepAliveTask(platform.getServerGBId());
try {
commanderForPlatform.unregister(platform, sipTransactionInfo, null, eventResult -> {
log.info("[国标级联] 注销成功, 平台:{}", platform.getServerGBId());
});
} catch (InvalidArgumentException | ParseException | SipException e) {
log.error("[命令发送失败] 国标级联: {}", e.getMessage());
}
}
// 定时监听国标级联所进行的WVP服务是否正常 如果异常则选择新的wvp执行 // 定时监听国标级联所进行的WVP服务是否正常 如果异常则选择新的wvp执行
@Scheduled(fixedDelay = 2, timeUnit = TimeUnit.SECONDS) //每3秒执行一次 @Scheduled(fixedDelay = 2, timeUnit = TimeUnit.SECONDS) //每3秒执行一次
public void execute(){ public void execute(){
@ -127,6 +194,7 @@ public class PlatformServiceImpl implements IPlatformService {
return; return;
} }
log.info("[集群] 检测到 {} 已离线", serverId); log.info("[集群] 检测到 {} 已离线", serverId);
redisCatchStorage.removeOfflineWVPInfo(serverId);
String chooseServerId = redisCatchStorage.chooseOneServer(serverId); String chooseServerId = redisCatchStorage.chooseOneServer(serverId);
if (!userSetting.getServerId().equals(chooseServerId)){ if (!userSetting.getServerId().equals(chooseServerId)){
return; return;
@ -139,20 +207,26 @@ public class PlatformServiceImpl implements IPlatformService {
platform.setAddress(getIpWithSameNetwork(platform.getAddress())); platform.setAddress(getIpWithSameNetwork(platform.getAddress()));
platform.setServerId(userSetting.getServerId()); platform.setServerId(userSetting.getServerId());
platformMapper.update(platform); platformMapper.update(platform);
// 更新redis // 检查就平台是否注册到期没有则注销由本平台重新注册
redisCatchStorage.delPlatformCatchInfo(platform.getServerGBId()); List<PlatformRegisterTaskInfo> taskInfoList = statusTaskRunner.getRegisterTransactionInfoByServerId(serverId);
PlatformCatch platformCatch = new PlatformCatch(); boolean needUnregister = false;
platformCatch.setPlatform(platform); SipTransactionInfo sipTransactionInfo = null;
platformCatch.setId(platform.getServerGBId()); if (!taskInfoList.isEmpty()) {
redisCatchStorage.updatePlatformCatchInfo(platformCatch); for (PlatformRegisterTaskInfo taskInfo : taskInfoList) {
if (taskInfo.getPlatformServerId().equals(platform.getServerGBId())
&& taskInfo.getSipTransactionInfo() != null) {
needUnregister = true;
sipTransactionInfo = taskInfo.getSipTransactionInfo();
break;
}
}
}
if (needUnregister) {
sendUnRegister(platform, sipTransactionInfo);
}else {
// 开始注册 // 开始注册
// 注册成功时由程序直接调用了online方法 // 注册成功时由程序直接调用了online方法
try { sendRegister(platform, null);
commanderForPlatform.register(platform, eventResult -> {
log.info("[国标级联] {}{},添加向上级注册失败,请确定上级平台可用时重新保存", platform.getName(), platform.getServerGBId());
}, null);
} catch (InvalidArgumentException | ParseException | SipException e) {
log.error("[命令发送失败] 国标级联: {}", e.getMessage());
} }
}); });
}); });
@ -265,25 +339,17 @@ public class PlatformServiceImpl implements IPlatformService {
} }
platform.setServerId(userSetting.getServerId()); platform.setServerId(userSetting.getServerId());
int result = platformMapper.add(platform); int result = platformMapper.add(platform);
// 添加缓存
PlatformCatch platformCatch = new PlatformCatch();
platformCatch.setPlatform(platform);
platformCatch.setId(platform.getServerGBId());
redisCatchStorage.updatePlatformCatchInfo(platformCatch);
if (platform.isEnable()) { if (platform.isEnable()) {
// 保存时启用就发送注册 // 保存时启用就发送注册
// 注册成功时由程序直接调用了online方法 // 注册成功时由程序直接调用了online方法
try { sendRegister(platform, null);
commanderForPlatform.register(platform, eventResult -> {
log.info("[国标级联] {}{},添加向上级注册失败,请确定上级平台可用时重新保存", platform.getName(), platform.getServerGBId());
}, null);
} catch (InvalidArgumentException | ParseException | SipException e) {
log.error("[命令发送失败] 国标级联: {}", e.getMessage());
}
} }
return result > 0; return result > 0;
} }
@Override @Override
public boolean update(Platform platform) { public boolean update(Platform platform) {
Assert.isTrue(platform.getId() > 0, "ID必须存在"); Assert.isTrue(platform.getId() > 0, "ID必须存在");
@ -294,52 +360,15 @@ public class PlatformServiceImpl implements IPlatformService {
if (!userSetting.getServerId().equals(platformInDb.getServerId())) { if (!userSetting.getServerId().equals(platformInDb.getServerId())) {
return redisRpcService.updatePlatform(platformInDb.getServerId(), platform); return redisRpcService.updatePlatform(platformInDb.getServerId(), platform);
} }
PlatformCatch platformCatchOld = redisCatchStorage.queryPlatformCatchInfo(platformInDb.getServerGBId());
platform.setUpdateTime(DateUtil.getNow());
// 停止心跳定时
final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + platformInDb.getServerGBId();
dynamicTask.stop(keepaliveTaskKey);
// 停止注册定时
final String registerTaskKey = REGISTER_KEY_PREFIX + platformInDb.getServerGBId();
dynamicTask.stop(registerTaskKey);
// 注销旧的
try {
if (platformInDb.isStatus() && platformCatchOld != null) {
log.info("保存平台{}时发现旧平台在线,发送注销命令", platformInDb.getServerGBId());
commanderForPlatform.unregister(platformInDb, platformCatchOld.getSipTransactionInfo(), null, eventResult -> {
log.info("[国标级联] 注销成功, 平台:{}", platformInDb.getServerGBId());
});
}
} catch (InvalidArgumentException | ParseException | SipException e) {
log.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
}
// 更新数据库 // 更新数据库
if (platform.getCatalogGroup() == 0) { if (platform.getCatalogGroup() == 0) {
platform.setCatalogGroup(1); platform.setCatalogGroup(1);
} }
platformMapper.update(platform); platformMapper.update(platform);
// 更新redis if (statusTaskRunner.containsRegister(platformInDb.getServerGBId())) {
redisCatchStorage.delPlatformCatchInfo(platformInDb.getServerGBId()); SipTransactionInfo transactionInfo = statusTaskRunner.getRegisterTransactionInfo(platformInDb.getServerGBId());
PlatformCatch platformCatch = new PlatformCatch(); // 注销后出发平台离线 如果是启用的平台那么下次丢失检测会检测到并重新注册上线
platformCatch.setPlatform(platform); sendUnRegister(platformInDb, transactionInfo);
platformCatch.setId(platform.getServerGBId());
redisCatchStorage.updatePlatformCatchInfo(platformCatch);
// 注册
if (platform.isEnable()) {
// 保存时启用就发送注册
// 注册成功时由程序直接调用了online方法
try {
log.info("[国标级联] 平台注册 {}", platform.getDeviceGBId());
commanderForPlatform.register(platform, eventResult -> {
log.info("[国标级联] {},添加向上级注册失败,请确定上级平台可用时重新保存", platform.getServerGBId());
}, null);
} catch (InvalidArgumentException | ParseException | SipException e) {
log.error("[命令发送失败] 国标级联: {}", e.getMessage());
}
} }
return false; return false;
@ -348,84 +377,85 @@ public class PlatformServiceImpl implements IPlatformService {
@Override @Override
public void online(Platform platform, SipTransactionInfo sipTransactionInfo) { public void online(Platform platform, SipTransactionInfo sipTransactionInfo) {
log.info("[国标级联]{}, 平台上线", platform.getServerGBId()); log.info("[国标级联]{}, 平台上线", platform.getServerGBId());
final String registerFailAgainTaskKey = REGISTER_FAIL_AGAIN_KEY_PREFIX + platform.getServerGBId(); PlatformRegisterTask registerTask = new PlatformRegisterTask(platform.getServerGBId(), platform.getExpires() * 1000L - 500L,
dynamicTask.stop(registerFailAgainTaskKey); sipTransactionInfo, (platformServerGbId) -> {
this.registerExpire(platformServerGbId, sipTransactionInfo);
});
statusTaskRunner.addRegisterTask(registerTask);
platformMapper.updateStatus(platform.getServerGBId(), true); PlatformKeepaliveTask keepaliveTask = new PlatformKeepaliveTask(platform.getServerGBId(), platform.getKeepTimeout() * 1000L,
PlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(platform.getServerGBId()); this::keepaliveExpire);
if (platformCatch == null) { statusTaskRunner.addKeepAliveTask(keepaliveTask);
platformCatch = new PlatformCatch(); platformMapper.updateStatus(platform.getId(), true, userSetting.getServerId());
platformCatch.setPlatform(platform);
platformCatch.setId(platform.getServerGBId()); if (platform.getAutoPushChannel() != null && platform.getAutoPushChannel()) {
platform.setStatus(true); if (subscribeHolder.getCatalogSubscribe(platform.getServerGBId()) == null) {
platformCatch.setPlatform(platform); log.info("[国标级联]{}, 添加自动通道推送模拟订阅信息", platform.getServerGBId());
addSimulatedSubscribeInfo(platform);
}
}else {
SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(platform.getServerGBId());
if (catalogSubscribe != null && catalogSubscribe.getExpires() == -1) {
subscribeHolder.removeCatalogSubscribe(platform.getServerGBId());
}
}
} }
platformCatch.getPlatform().setStatus(true); /**
platformCatch.setSipTransactionInfo(sipTransactionInfo); * 注册到期处理
redisCatchStorage.updatePlatformCatchInfo(platformCatch); */
private void registerExpire(String platformServerId, SipTransactionInfo transactionInfo) {
final String registerTaskKey = REGISTER_KEY_PREFIX + platform.getServerGBId(); log.info("[国标级联] 注册到期, 上级平台编号: {}", platformServerId);
if (!dynamicTask.isAlive(registerTaskKey)) { Platform platform = queryPlatformByServerGBId(platformServerId);
log.info("[国标级联]{}, 添加定时注册任务", platform.getServerGBId()); if (platform == null || !platform.isEnable()) {
// 添加注册任务 log.info("[国标级联] 注册到期, 上级平台编号: {}, 平台不存在或者未启用, 忽略", platformServerId);
dynamicTask.startCron(registerTaskKey, return;
// 注册失败注册成功时由程序直接调用了online方法 }
()-> registerTask(platform, sipTransactionInfo), sendRegister(platform, transactionInfo);
platform.getExpires() * 1000);
} }
private void keepaliveExpire(String platformServerId, int failCount) {
final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + platform.getServerGBId(); log.info("[国标级联] 心跳到期, 上级平台编号: {}", platformServerId);
if (!dynamicTask.contains(keepaliveTaskKey)) { Platform platform = queryPlatformByServerGBId(platformServerId);
log.info("[国标级联]{}, 添加定时心跳任务", platform.getServerGBId()); if (platform == null || !platform.isEnable()) {
// 添加心跳任务 log.info("[国标级联] 心跳到期, 上级平台编号: {}, 平台不存在或者未启用, 忽略", platformServerId);
dynamicTask.startCron(keepaliveTaskKey, return;
()-> { }
try { try {
commanderForPlatform.keepalive(platform, eventResult -> { commanderForPlatform.keepalive(platform, eventResult -> {
// 心跳失败 // 心跳失败
if (eventResult.type != SipSubscribe.EventResultType.timeout) { if (eventResult.type != SipSubscribe.EventResultType.timeout) {
log.warn("[国标级联] 发送心跳收到错误code {}, msg: {}", eventResult.statusCode, eventResult.msg); log.warn("[国标级联] 发送心跳收到错误code {}, msg: {}", eventResult.statusCode, eventResult.msg);
} }
// 心跳失败
PlatformCatch platformCatchForNow = redisCatchStorage.queryPlatformCatchInfo(platform.getServerGBId());
// 此时是第三次心跳超时 平台离线
if (platformCatchForNow.getKeepAliveReply() == 2) {
// 设置平台离线并重新注册
log.info("[国标级联] 三次心跳失败, 平台{}({})离线", platform.getName(), platform.getServerGBId());
offline(platform, false);
}else {
platformCatchForNow.setKeepAliveReply(platformCatchForNow.getKeepAliveReply() + 1);
redisCatchStorage.updatePlatformCatchInfo(platformCatchForNow);
}
}, eventResult -> { // 心跳超时失败
// 心跳成功 if (failCount < 2) {
// 清空之前的心跳超时计数 log.info("[国标级联] 心跳发送超时, 平台服务编号: {}", platformServerId);
PlatformCatch platformCatchForNow = redisCatchStorage.queryPlatformCatchInfo(platform.getServerGBId()); PlatformKeepaliveTask keepaliveTask = new PlatformKeepaliveTask(platform.getServerGBId(), platform.getKeepTimeout() * 1000L,
if (platformCatchForNow != null && platformCatchForNow.getKeepAliveReply() > 0) { this::keepaliveExpire);
platformCatchForNow.setKeepAliveReply(0); keepaliveTask.setFailCount(failCount + 1);
redisCatchStorage.updatePlatformCatchInfo(platformCatchForNow); statusTaskRunner.addKeepAliveTask(keepaliveTask);
}else {
// 心跳超时三次, 不再发送心跳 平台离线
log.info("[国标级联] 心跳发送超时三次,平台离线, 平台服务编号: {}", platformServerId);
offline(platform);
} }
log.info("[国标级联] 发送心跳,平台{}({}), code {}, msg: {}", platform.getName(), platform.getServerGBId(), eventResult.statusCode, eventResult.msg); }, eventResult -> {
PlatformKeepaliveTask keepaliveTask = new PlatformKeepaliveTask(platform.getServerGBId(), platform.getKeepTimeout() * 1000L,
this::keepaliveExpire);
statusTaskRunner.addKeepAliveTask(keepaliveTask);
}); });
} catch (SipException | InvalidArgumentException | ParseException e) { } catch (SipException | InvalidArgumentException | ParseException e) {
log.error("[命令发送失败] 国标级联 发送心跳: {}", e.getMessage()); log.error("[命令发送失败] 国标级联 发送心跳: {}", e.getMessage());
} if (failCount < 2) {
}, PlatformKeepaliveTask keepaliveTask = new PlatformKeepaliveTask(platform.getServerGBId(), platform.getKeepTimeout() * 1000L,
(platform.getKeepTimeout())*1000); this::keepaliveExpire);
} keepaliveTask.setFailCount(failCount + 1);
if (platform.getAutoPushChannel() != null && platform.getAutoPushChannel()) { statusTaskRunner.addKeepAliveTask(keepaliveTask);
if (subscribeHolder.getCatalogSubscribe(platform.getServerGBId()) == null) {
log.info("[国标级联]{}, 添加自动通道推送模拟订阅信息", platform.getServerGBId());
addSimulatedSubscribeInfo(platform);
}
}else { }else {
SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(platform.getServerGBId()); // 心跳超时三次, 不再发送心跳 平台离线
if (catalogSubscribe != null && catalogSubscribe.getExpires() == -1) { log.info("[国标级联] 心跳发送失败三次,平台离线, 平台服务编号: {}", platformServerId);
subscribeHolder.removeCatalogSubscribe(platform.getServerGBId()); offline(platform);
} }
} }
} }
@ -437,87 +467,25 @@ public class PlatformServiceImpl implements IPlatformService {
SubscribeInfo.buildSimulated(platform.getServerGBId(), platform.getServerIp())); SubscribeInfo.buildSimulated(platform.getServerGBId(), platform.getServerIp()));
} }
private void registerTask(Platform platform, SipTransactionInfo sipTransactionInfo){
try {
// 不在同一个会话中续订则每次全新注册
if (!userSetting.isRegisterKeepIntDialog()) {
sipTransactionInfo = null;
}
if (sipTransactionInfo == null) {
log.info("[国标级联] 平台:{}注册即将到期,开始重新注册", platform.getServerGBId());
}else {
log.info("[国标级联] 平台:{}注册即将到期,开始续订", platform.getServerGBId());
}
commanderForPlatform.register(platform, sipTransactionInfo, eventResult -> {
log.info("[国标级联] 平台:{}注册失败,{}:{}", platform.getServerGBId(),
eventResult.statusCode, eventResult.msg);
if (platform.isStatus()) {
offline(platform, false);
}
}, null);
} catch (Exception e) {
log.error("[命令发送失败] 国标级联定时注册: {}", e.getMessage());
}
}
@Override @Override
public void offline(Platform platform, boolean stopRegister) { public void offline(Platform platform) {
log.info("[平台离线]{}({})", platform.getName(), platform.getServerGBId()); log.info("[平台离线]{}({})", platform.getName(), platform.getServerGBId());
PlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(platform.getServerGBId()); statusTaskRunner.removeRegisterTask(platform.getServerGBId());
platformCatch.setKeepAliveReply(0); statusTaskRunner.removeKeepAliveTask(platform.getServerGBId());
platformCatch.setRegisterAliveReply(0);
Platform catchPlatform = platformCatch.getPlatform(); subscribeHolder.removeCatalogSubscribe(platform.getServerGBId());
catchPlatform.setStatus(false); subscribeHolder.removeMobilePositionSubscribe(platform.getServerGBId());
platformCatch.setPlatform(catchPlatform);
redisCatchStorage.updatePlatformCatchInfo(platformCatch); platformMapper.updateStatus(platform.getId(), false, userSetting.getServerId());
platformMapper.updateStatus(platform.getServerGBId(), false);
// 停止所有推流 // 停止所有推流
log.info("[平台离线] {}({}), 停止所有推流", platform.getName(), platform.getServerGBId()); log.info("[平台离线] {}({}), 停止所有推流", platform.getName(), platform.getServerGBId());
stopAllPush(platform.getServerGBId()); stopAllPush(platform.getServerGBId());
// 清除注册定时
log.info("[平台离线] {}({}), 停止定时注册任务", platform.getName(), platform.getServerGBId());
final String registerTaskKey = REGISTER_KEY_PREFIX + platform.getServerGBId();
if (dynamicTask.contains(registerTaskKey)) {
dynamicTask.stop(registerTaskKey);
}
// 清除心跳定时
log.info("[平台离线] {}({}), 停止定时发送心跳任务", platform.getName(), platform.getServerGBId());
final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + platform.getServerGBId();
if (dynamicTask.contains(keepaliveTaskKey)) {
// 清除心跳任务
dynamicTask.stop(keepaliveTaskKey);
}
// 停止订阅回复
SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(platform.getServerGBId());
if (catalogSubscribe != null) {
if (catalogSubscribe.getExpires() > 0) {
log.info("[平台离线] {}({}), 停止目录订阅回复", platform.getName(), platform.getServerGBId());
subscribeHolder.removeCatalogSubscribe(platform.getServerGBId());
}
}
log.info("[平台离线] {}({}), 停止移动位置订阅回复", platform.getName(), platform.getServerGBId());
subscribeHolder.removeMobilePositionSubscribe(platform.getServerGBId());
// 发起定时自动重新注册
if (!stopRegister) {
// 设置为60秒自动尝试重新注册
final String registerFailAgainTaskKey = REGISTER_FAIL_AGAIN_KEY_PREFIX + platform.getServerGBId();
Platform platformInDb = platformMapper.query(platform.getId());
if (platformInDb.isEnable()) {
dynamicTask.startCron(registerFailAgainTaskKey,
()-> registerTask(platformInDb, null),
userSetting.getRegisterAgainAfterTime() * 1000);
}
}
} }
private void stopAllPush(String platformId) { private void stopAllPush(String platformId) {
List<SendRtpInfo> sendRtpItems = sendRtpServerService.queryForPlatform(platformId); List<SendRtpInfo> sendRtpItems = sendRtpServerService.queryForPlatform(platformId);
if (sendRtpItems != null && sendRtpItems.size() > 0) { if (sendRtpItems != null && !sendRtpItems.isEmpty()) {
for (SendRtpInfo sendRtpItem : sendRtpItems) { for (SendRtpInfo sendRtpItem : sendRtpItems) {
ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc()); ssrcFactory.releaseSsrc(sendRtpItem.getMediaServerId(), sendRtpItem.getSsrc());
sendRtpServerService.delete(sendRtpItem); sendRtpServerService.delete(sendRtpItem);
@ -527,23 +495,6 @@ public class PlatformServiceImpl implements IPlatformService {
} }
} }
@Override
public void login(Platform platform) {
final String registerTaskKey = REGISTER_KEY_PREFIX + platform.getServerGBId();
try {
commanderForPlatform.register(platform, eventResult1 -> {
log.info("[国标级联] {}开始定时发起注册间隔为1分钟", platform.getServerGBId());
// 添加注册任务
dynamicTask.startCron(registerTaskKey,
// 注册失败注册成功时由程序直接调用了online方法
()-> log.info("[国标级联] {}({}),平台离线后持续发起注册,失败", platform.getName(), platform.getServerGBId()),
60*1000);
}, null);
} catch (InvalidArgumentException | ParseException | SipException e) {
log.error("[命令发送失败] 国标级联注册: {}", e.getMessage());
}
}
@Override @Override
public void sendNotifyMobilePosition(String platformId) { public void sendNotifyMobilePosition(String platformId) {
Platform platform = platformMapper.getParentPlatByServerGBId(platformId); Platform platform = platformMapper.getParentPlatByServerGBId(platformId);
@ -559,12 +510,25 @@ public class PlatformServiceImpl implements IPlatformService {
} }
for (CommonGBChannel channel : channelList) { for (CommonGBChannel channel : channelList) {
GPSMsgInfo gpsMsgInfo = redisCatchStorage.getGpsMsgInfo(channel.getGbDeviceId()); GPSMsgInfo gpsMsgInfo = redisCatchStorage.getGpsMsgInfo(channel.getGbDeviceId());
// 无最新位置则发送当前位置
if (gpsMsgInfo != null && (gpsMsgInfo.getLng() == 0 && gpsMsgInfo.getLat() == 0)) {
gpsMsgInfo = null;
}
if (gpsMsgInfo == null && !userSetting.isSendPositionOnDemand()){
gpsMsgInfo = new GPSMsgInfo();
gpsMsgInfo.setId(channel.getGbDeviceId());
gpsMsgInfo.setLng(channel.getGbLongitude());
gpsMsgInfo.setLat(channel.getGbLatitude());
gpsMsgInfo.setAltitude(channel.getGpsAltitude());
gpsMsgInfo.setSpeed(channel.getGpsSpeed());
gpsMsgInfo.setDirection(channel.getGpsDirection());
gpsMsgInfo.setTime(channel.getGpsTime());
}
// 无最新位置不发送 // 无最新位置不发送
if (gpsMsgInfo != null) { if (gpsMsgInfo != null) {
// 经纬度都为0不发送
if (gpsMsgInfo.getLng() == 0 && gpsMsgInfo.getLat() == 0) {
continue;
}
// 发送GPS消息 // 发送GPS消息
try { try {
commanderForPlatform.sendNotifyMobilePosition(platform, gpsMsgInfo, channel, subscribe); commanderForPlatform.sendNotifyMobilePosition(platform, gpsMsgInfo, channel, subscribe);
@ -877,7 +841,7 @@ public class PlatformServiceImpl implements IPlatformService {
@Override @Override
public List<Platform> queryEnablePlatformList(String serverId) { public List<Platform> queryEnablePlatformList(String serverId) {
return platformMapper.queryEnableParentPlatformList(serverId,true); return platformMapper.queryEnableParentPlatformListByServerId(serverId,true);
} }
@Override @Override
@ -885,55 +849,23 @@ public class PlatformServiceImpl implements IPlatformService {
public void delete(Integer platformId, CommonCallback<Object> callback) { public void delete(Integer platformId, CommonCallback<Object> callback) {
Platform platform = platformMapper.query(platformId); Platform platform = platformMapper.query(platformId);
Assert.notNull(platform, "平台不存在"); Assert.notNull(platform, "平台不存在");
// 发送离线消息,无论是否成功都删除缓存 if (statusTaskRunner.containsRegister(platform.getServerGBId())) {
PlatformCatch platformCatch = redisCatchStorage.queryPlatformCatchInfo(platform.getServerGBId());
if (platformCatch != null) {
String key = UUID.randomUUID().toString();
dynamicTask.startDelay(key, ()->{
deletePlatformInfo(platform);
if (callback != null) {
callback.run(null);
}
}, 2000);
try { try {
commanderForPlatform.unregister(platform, platformCatch.getSipTransactionInfo(), (event -> { SipTransactionInfo transactionInfo = statusTaskRunner.getRegisterTransactionInfo(platform.getServerGBId());
dynamicTask.stop(key); sendUnRegister(platform, transactionInfo);
// 移除平台相关的信息 }catch (Exception ignored) {}
deletePlatformInfo(platform);
if (callback != null) {
callback.run(null);
} }
}), (event -> {
dynamicTask.stop(key);
// 移除平台相关的信息
deletePlatformInfo(platform);
if (callback != null) {
callback.run(null);
}
}));
} catch (InvalidArgumentException | ParseException | SipException e) {
log.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
}
}else {
deletePlatformInfo(platform);
if (callback != null) {
callback.run(null);
}
}
}
@Transactional
public void deletePlatformInfo(Platform platform) {
// 删除关联的通道
platformChannelMapper.removeChannelsByPlatformId(platform.getId());
// 删除关联的分组
platformChannelMapper.removePlatformGroupsByPlatformId(platform.getId());
// 删除关联的行政区划
platformChannelMapper.removePlatformRegionByPlatformId(platform.getId());
// 删除redis缓存
redisCatchStorage.delPlatformCatchInfo(platform.getServerGBId());
// 删除平台信息
platformMapper.delete(platform.getId()); platformMapper.delete(platform.getId());
statusTaskRunner.removeRegisterTask(platform.getServerGBId());
statusTaskRunner.removeKeepAliveTask(platform.getServerGBId());
subscribeHolder.removeCatalogSubscribe(platform.getServerGBId());
subscribeHolder.removeMobilePositionSubscribe(platform.getServerGBId());
}
@Override
public List<Platform> queryAll(String serverId) {
return platformMapper.queryByServerId(serverId);
} }
} }

View File

@ -339,7 +339,7 @@ public class PlayServiceImpl implements IPlayService {
InviteInfo inviteInfoInCatch = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); InviteInfo inviteInfoInCatch = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId());
if (inviteInfoInCatch != null ) { if (inviteInfoInCatch != null ) {
if (inviteInfoInCatch.getStreamInfo() == null) { if (inviteInfoInCatch.getStreamInfo() == null) {
// 释放生成的ssrc使用上一次申请的322 // 释放生成的ssrc使用上一次申请的
ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrc); ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrc);
// 点播发起了但是尚未成功, 仅注册回调等待结果即可 // 点播发起了但是尚未成功, 仅注册回调等待结果即可
@ -1723,7 +1723,14 @@ public class PlayServiceImpl implements IPlayService {
throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error"); throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error");
} }
DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(channel.getGbId()); DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(channel.getGbId());
play(device, deviceChannel, callback);
MediaServer mediaServerItem = getNewMediaServerItem(device);
if (mediaServerItem == null) {
log.warn("[点播] 未找到可用的zlm deviceId: {},channelId:{}", device.getDeviceId(), deviceChannel.getDeviceId());
throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的zlm");
}
play(mediaServerItem, device, deviceChannel, null, record, callback);
} }
@Override @Override

View File

@ -262,4 +262,66 @@ public class RegionServiceImpl implements IRegionService {
regionList.addAll(allParent); regionList.addAll(allParent);
return regionList; return regionList;
} }
@Override
public String getDescription(String civilCode) {
CivilCodePo civilCodePo = CivilCodeUtil.INSTANCE.getCivilCodePo(civilCode);
Assert.notNull(civilCodePo, String.format("节点%s未查询到", civilCode));
StringBuilder sb = new StringBuilder();
sb.append(civilCodePo.getName());
List<CivilCodePo> civilCodePoList = CivilCodeUtil.INSTANCE.getAllParentCode(civilCode);
if (civilCodePoList.isEmpty()) {
return sb.toString();
}
for (int i = 0; i < civilCodePoList.size(); i++) {
CivilCodePo item = civilCodePoList.get(i);
sb.insert(0, item.getName());
if (i != civilCodePoList.size() - 1) {
sb.insert(0, "/");
}
}
return sb.toString();
}
@Override
@Transactional
public void addByCivilCode(String civilCode) {
CivilCodePo civilCodePo = CivilCodeUtil.INSTANCE.getCivilCodePo(civilCode);
// 查询是否已经存在此节点
Assert.notNull(civilCodePo, String.format("节点%s未查询到", civilCode));
List<CivilCodePo> civilCodePoList = CivilCodeUtil.INSTANCE.getAllParentCode(civilCode);
civilCodePoList.add(civilCodePo);
Set<String> civilCodeSet = regionMapper.queryInCivilCodePoList(civilCodePoList);
if (!civilCodeSet.isEmpty()) {
civilCodePoList.removeIf(item -> civilCodeSet.contains(item.getCode()));
}
if (civilCodePoList.isEmpty()) {
return;
}
int parentId = -1;
for (int i = civilCodePoList.size() - 1; i > -1; i--) {
CivilCodePo codePo = civilCodePoList.get(i);
Region region = new Region();
region.setDeviceId(codePo.getCode());
region.setParentDeviceId(codePo.getParentCode());
region.setName(civilCodePo.getName());
region.setCreateTime(DateUtil.getNow());
region.setUpdateTime(DateUtil.getNow());
if (parentId == -1 && codePo.getParentCode() != null) {
Region parentRegion = regionMapper.queryByDeviceId(codePo.getParentCode());
if (parentRegion == null){
log.error(String.format("行政区划%sy已存在但查询错误", codePo.getParentCode()));
throw new ControllerException(ErrorCode.ERROR100.getCode(), String.format("行政区划%sy已存在但查询错误", codePo.getParentCode()));
}
region.setParentId(parentRegion.getId());
}else {
region.setParentId(parentId);
}
regionMapper.add(region);
parentId = region.getId();
}
}
} }

View File

@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.session;
import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.UserSetting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -13,6 +14,7 @@ import java.util.Set;
/** /**
* ssrc使用 * ssrc使用
*/ */
@Slf4j
@Component @Component
public class SSRCFactory { public class SSRCFactory {
@ -93,6 +95,7 @@ public class SSRCFactory {
String redisKey = SSRC_INFO_KEY + userSetting.getServerId() + "_" + mediaServerId; String redisKey = SSRC_INFO_KEY + userSetting.getServerId() + "_" + mediaServerId;
Long size = redisTemplate.opsForSet().size(redisKey); Long size = redisTemplate.opsForSet().size(redisKey);
if (size == null || size == 0) { if (size == null || size == 0) {
log.info("[获取 SSRC 失败] redisKey {}", redisKey);
throw new RuntimeException("ssrc已经用完"); throw new RuntimeException("ssrc已经用完");
} else { } else {
// 在集合中移除并返回一个随机成员 // 在集合中移除并返回一个随机成员

View File

@ -1,10 +0,0 @@
package com.genersoft.iot.vmp.gb28181.task;
import com.genersoft.iot.vmp.common.CommonCallback;
/**
* @author lin
*/
public interface ISubscribeTask extends Runnable{
void stop(CommonCallback<Boolean> callback);
}

View File

@ -0,0 +1,49 @@
package com.genersoft.iot.vmp.gb28181.task.deviceSubscribe;
import com.genersoft.iot.vmp.common.SubscribeCallback;
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import lombok.Data;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
@Data
public abstract class SubscribeTask implements Delayed {
private String deviceId;
private SubscribeCallback callback;
private SipTransactionInfo transactionInfo;
/**
* 超时时间(单位 毫秒)
*/
private long delayTime;
public abstract void expired();
public abstract String getKey();
public abstract String getName();
@Override
public long getDelay(@NotNull TimeUnit unit) {
return unit.convert(delayTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(@NotNull Delayed o) {
return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
}
public SubscribeTaskInfo getInfo(){
SubscribeTaskInfo subscribeTaskInfo = new SubscribeTaskInfo();
subscribeTaskInfo.setName(getName());
subscribeTaskInfo.setTransactionInfo(transactionInfo);
subscribeTaskInfo.setDeviceId(deviceId);
subscribeTaskInfo.setKey(getKey());
return subscribeTaskInfo;
}
}

View File

@ -0,0 +1,22 @@
package com.genersoft.iot.vmp.gb28181.task.deviceSubscribe;
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import lombok.Data;
@Data
public class SubscribeTaskInfo {
private String deviceId;
private SipTransactionInfo transactionInfo;
private String name;
private String key;
/**
* 过期时间
*/
private long expireTime;
}

View File

@ -0,0 +1,135 @@
package com.genersoft.iot.vmp.gb28181.task.deviceSubscribe;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class SubscribeTaskRunner{
private final Map<String, SubscribeTask> subscribes = new ConcurrentHashMap<>();
private final DelayQueue<SubscribeTask> delayQueue = new DelayQueue<>();
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
@Autowired
private UserSetting userSetting;
private final String prefix = "VMP_DEVICE_SUBSCRIBE";
// 订阅过期检查
@Scheduled(fixedDelay = 500, timeUnit = TimeUnit.MILLISECONDS)
public void expirationCheck(){
while (!delayQueue.isEmpty()) {
SubscribeTask take = null;
try {
take = delayQueue.take();
try {
removeSubscribe(take.getKey());
take.expired();
}catch (Exception e) {
log.error("[设备订阅到期] {} 到期处理时出现异常, 设备编号: {} ", take.getName(), take.getDeviceId());
}
} catch (InterruptedException e) {
log.error("[设备订阅任务] ", e);
}
}
}
public void addSubscribe(SubscribeTask task) {
Duration duration = Duration.ofSeconds((task.getDelayTime() - System.currentTimeMillis())/1000);
if (duration.getSeconds() < 0) {
return;
}
subscribes.put(task.getKey(), task);
String key = String.format("%s_%s_%s", prefix, userSetting.getServerId(), task.getKey());
redisTemplate.opsForValue().set(key, task.getInfo(), duration);
delayQueue.offer(task);
}
public boolean removeSubscribe(String key) {
SubscribeTask task = subscribes.get(key);
if (task == null) {
return false;
}
String redisKey = String.format("%s_%s_%s", prefix, userSetting.getServerId(), task.getKey());
redisTemplate.delete(redisKey);
subscribes.remove(key);
if (delayQueue.contains(task)) {
boolean remove = delayQueue.remove(task);
if (!remove) {
log.info("[移除订阅任务] 从延时队列内移除失败: {}", key);
}
}
return true;
}
public SipTransactionInfo getTransactionInfo(String key) {
SubscribeTask task = subscribes.get(key);
if (task == null) {
return null;
}
return task.getTransactionInfo();
}
public boolean updateDelay(String key, long expirationTime) {
SubscribeTask task = subscribes.get(key);
if (task == null) {
return false;
}
log.info("[更新订阅任务时间] {}, 编号: {}", task.getName(), key);
if (delayQueue.contains(task)) {
boolean remove = delayQueue.remove(task);
if (!remove) {
log.info("[更新订阅任务时间] 从延时队列内移除失败: {}", key);
}
}
task.setDelayTime(expirationTime);
delayQueue.offer(task);
String redisKey = String.format("%s_%s_%s", prefix, userSetting.getServerId(), task.getKey());
Duration duration = Duration.ofSeconds((expirationTime - System.currentTimeMillis())/1000);
redisTemplate.expire(redisKey, duration);
return true;
}
public boolean containsKey(String key) {
return subscribes.containsKey(key);
}
public List<SubscribeTaskInfo> getAllTaskInfo(){
String scanKey = String.format("%s_%s_*", prefix, userSetting.getServerId());
List<Object> values = RedisUtil.scan(redisTemplate, scanKey);
if (values.isEmpty()) {
return new ArrayList<>();
}
List<SubscribeTaskInfo> result = new ArrayList<>();
for (Object value : values) {
String redisKey = (String)value;
SubscribeTaskInfo taskInfo = (SubscribeTaskInfo)redisTemplate.opsForValue().get(redisKey);
if (taskInfo == null) {
continue;
}
Long expire = redisTemplate.getExpire(redisKey);
taskInfo.setExpireTime(expire);
result.add(taskInfo);
}
return result;
}
}

View File

@ -0,0 +1,48 @@
package com.genersoft.iot.vmp.gb28181.task.deviceSubscribe.impl;
import com.genersoft.iot.vmp.common.SubscribeCallback;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import com.genersoft.iot.vmp.gb28181.task.deviceSubscribe.SubscribeTask;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SubscribeTaskForCatalog extends SubscribeTask {
public static final String name = "catalog";
public static SubscribeTask getInstance(Device device, SubscribeCallback callback, SipTransactionInfo transactionInfo) {
if (device.getSubscribeCycleForCatalog() <= 0) {
return null;
}
SubscribeTaskForCatalog subscribeTaskForCatalog = new SubscribeTaskForCatalog();
subscribeTaskForCatalog.setDelayTime((device.getSubscribeCycleForCatalog() * 1000L - 500L) + System.currentTimeMillis());
subscribeTaskForCatalog.setDeviceId(device.getDeviceId());
subscribeTaskForCatalog.setCallback(callback);
subscribeTaskForCatalog.setTransactionInfo(transactionInfo);
return subscribeTaskForCatalog;
}
@Override
public void expired() {
if (super.getCallback() == null) {
log.info("[设备订阅到期] 目录订阅 未找到到期处理回调, 编号: {}", getDeviceId());
return;
}
getCallback().run(getDeviceId(), getTransactionInfo());
}
@Override
public String getKey() {
return String.format("%s_%s", name, getDeviceId());
}
@Override
public String getName() {
return name;
}
public static String getKey(Device device) {
return String.format("%s_%s", SubscribeTaskForCatalog.name, device.getDeviceId());
}
}

View File

@ -0,0 +1,48 @@
package com.genersoft.iot.vmp.gb28181.task.deviceSubscribe.impl;
import com.genersoft.iot.vmp.common.SubscribeCallback;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import com.genersoft.iot.vmp.gb28181.task.deviceSubscribe.SubscribeTask;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class SubscribeTaskForMobilPosition extends SubscribeTask {
public static final String name = "mobilPosition";
public static SubscribeTask getInstance(Device device, SubscribeCallback callback, SipTransactionInfo transactionInfo) {
if (device.getSubscribeCycleForCatalog() <= 0) {
return null;
}
SubscribeTaskForMobilPosition subscribeTaskForMobilPosition = new SubscribeTaskForMobilPosition();
subscribeTaskForMobilPosition.setDelayTime((device.getSubscribeCycleForMobilePosition() * 1000L - 500L) + System.currentTimeMillis());
subscribeTaskForMobilPosition.setDeviceId(device.getDeviceId());
subscribeTaskForMobilPosition.setCallback(callback);
subscribeTaskForMobilPosition.setTransactionInfo(transactionInfo);
return subscribeTaskForMobilPosition;
}
@Override
public void expired() {
if (super.getCallback() == null) {
log.info("[设备订阅到期] 移动位置订阅 未找到到期处理回调, 编号: {}", getDeviceId());
return;
}
getCallback().run(getDeviceId(), getTransactionInfo());
}
@Override
public String getKey() {
return String.format("%s_%s", name, getDeviceId());
}
@Override
public String getName() {
return name;
}
public static String getKey(Device device) {
return String.format("%s_%s", SubscribeTaskForMobilPosition.name, device.getDeviceId());
}
}

View File

@ -1,107 +0,0 @@
package com.genersoft.iot.vmp.gb28181.task.impl;
import com.genersoft.iot.vmp.common.CommonCallback;
import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import gov.nist.javax.sip.message.SIPRequest;
import lombok.extern.slf4j.Slf4j;
import javax.sip.DialogState;
import javax.sip.InvalidArgumentException;
import javax.sip.ResponseEvent;
import javax.sip.SipException;
import javax.sip.header.ToHeader;
import java.text.ParseException;
/**
* 目录订阅任务
* @author lin
*/
@Slf4j
public class CatalogSubscribeTask implements ISubscribeTask {
private final Device device;
private final ISIPCommander sipCommander;
private SIPRequest request;
private final DynamicTask dynamicTask;
private final String taskKey = "catalog-subscribe-timeout";
public CatalogSubscribeTask(Device device, ISIPCommander sipCommander, DynamicTask dynamicTask) {
this.device = device;
this.sipCommander = sipCommander;
this.dynamicTask = dynamicTask;
}
@Override
public void run() {
if (dynamicTask.get(taskKey) != null) {
dynamicTask.stop(taskKey);
}
SIPRequest sipRequest = null;
try {
sipRequest = sipCommander.catalogSubscribe(device, request, eventResult -> {
ResponseEvent event = (ResponseEvent) eventResult.event;
// 成功
log.info("[目录订阅]成功: {}", device.getDeviceId());
ToHeader toHeader = (ToHeader)event.getResponse().getHeader(ToHeader.NAME);
try {
this.request.getToHeader().setTag(toHeader.getTag());
} catch (ParseException e) {
log.info("[目录订阅]成功: 但为request设置ToTag失败");
this.request = null;
}
},eventResult -> {
this.request = null;
// 失败
log.warn("[目录订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg);
dynamicTask.startDelay(taskKey, CatalogSubscribeTask.this, 2000);
});
} catch (InvalidArgumentException | SipException | ParseException e) {
log.error("[命令发送失败] 目录订阅: {}", e.getMessage());
}
if (sipRequest != null) {
this.request = sipRequest;
}
}
@Override
public void stop(CommonCallback<Boolean> callback) {
/**
* dialog 的各个状态
* EARLY-> Early state状态-初始请求发送以后收到了一个临时响应消息
* CONFIRMED-> Confirmed Dialog状态-已确认
* COMPLETED-> Completed Dialog状态-已完成
* TERMINATED-> Terminated Dialog状态-终止
*/
log.info("取消目录订阅时dialog状态为{}", DialogState.CONFIRMED);
if (dynamicTask.get(taskKey) != null) {
dynamicTask.stop(taskKey);
}
device.setSubscribeCycleForCatalog(0);
try {
sipCommander.catalogSubscribe(device, request, eventResult -> {
ResponseEvent event = (ResponseEvent) eventResult.event;
if (event.getResponse().getRawContent() != null) {
// 成功
log.info("[取消目录订阅]成功: {}", device.getDeviceId());
}else {
// 成功
log.info("[取消目录订阅]成功: {}", device.getDeviceId());
}
if (callback != null) {
callback.run(event.getResponse().getRawContent() != null);
}
},eventResult -> {
// 失败
log.warn("[取消目录订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg);
});
} catch (InvalidArgumentException | SipException | ParseException e) {
log.error("[命令发送失败] 取消目录订阅: {}", e.getMessage());
}
}
}

View File

@ -1,103 +0,0 @@
package com.genersoft.iot.vmp.gb28181.task.impl;
import com.genersoft.iot.vmp.common.CommonCallback;
import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.task.ISubscribeTask;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import gov.nist.javax.sip.message.SIPRequest;
import lombok.extern.slf4j.Slf4j;
import javax.sip.InvalidArgumentException;
import javax.sip.ResponseEvent;
import javax.sip.SipException;
import javax.sip.header.ToHeader;
import java.text.ParseException;
/**
* 移动位置订阅的定时更新
* @author lin
*/
@Slf4j
public class MobilePositionSubscribeTask implements ISubscribeTask {
private final Device device;
private final ISIPCommander sipCommander;
private SIPRequest request;
private final DynamicTask dynamicTask;
private final String taskKey = "mobile-position-subscribe-timeout";
public MobilePositionSubscribeTask(Device device, ISIPCommander sipCommander, DynamicTask dynamicTask) {
this.device = device;
this.sipCommander = sipCommander;
this.dynamicTask = dynamicTask;
}
@Override
public void run() {
if (dynamicTask.get(taskKey) != null) {
dynamicTask.stop(taskKey);
}
SIPRequest sipRequest = null;
try {
sipRequest = sipCommander.mobilePositionSubscribe(device, request, eventResult -> {
// 成功
log.info("[移动位置订阅]成功: {}", device.getDeviceId());
ResponseEvent event = (ResponseEvent) eventResult.event;
ToHeader toHeader = (ToHeader)event.getResponse().getHeader(ToHeader.NAME);
try {
this.request.getToHeader().setTag(toHeader.getTag());
} catch (ParseException e) {
log.info("[移动位置订阅]成功: 为request设置ToTag失败");
this.request = null;
}
},eventResult -> {
this.request = null;
// 失败
log.warn("[移动位置订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg);
dynamicTask.startDelay(taskKey, MobilePositionSubscribeTask.this, 2000);
});
} catch (InvalidArgumentException | SipException | ParseException e) {
log.error("[命令发送失败] 移动位置订阅: {}", e.getMessage());
}
if (sipRequest != null) {
this.request = sipRequest;
}
}
@Override
public void stop(CommonCallback<Boolean> callback) {
/**
* dialog 的各个状态
* EARLY-> Early state状态-初始请求发送以后收到了一个临时响应消息
* CONFIRMED-> Confirmed Dialog状态-已确认
* COMPLETED-> Completed Dialog状态-已完成
* TERMINATED-> Terminated Dialog状态-终止
*/
if (dynamicTask.get(taskKey) != null) {
dynamicTask.stop(taskKey);
}
device.setSubscribeCycleForMobilePosition(0);
try {
sipCommander.mobilePositionSubscribe(device, request, eventResult -> {
ResponseEvent event = (ResponseEvent) eventResult.event;
if (event.getResponse().getRawContent() != null) {
// 成功
log.info("[取消移动位置订阅]成功: {}", device.getDeviceId());
}else {
// 成功
log.info("[取消移动位置订阅]成功: {}", device.getDeviceId());
}
if (callback != null) {
callback.run(event.getResponse().getRawContent() != null);
}
},eventResult -> {
// 失败
log.warn("[取消移动位置订阅]失败,信令发送失败: {}-{} ", device.getDeviceId(), eventResult.msg);
});
} catch (InvalidArgumentException | SipException | ParseException e) {
log.error("[命令发送失败] 取消移动位置订阅: {}", e.getMessage());
}
}
}

View File

@ -0,0 +1,64 @@
package com.genersoft.iot.vmp.gb28181.task.platformStatus;
import com.genersoft.iot.vmp.gb28181.bean.PlatformKeepaliveCallback;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
* 平台心跳任务
*/
@Slf4j
public class PlatformKeepaliveTask implements Delayed {
@Getter
private String platformServerId;
/**
* 超时时间(单位 毫秒)
*/
@Getter
@Setter
private long delayTime;
/**
* 到期回调
*/
@Getter
private PlatformKeepaliveCallback callback;
/**
* 心跳发送失败次数
*/
@Getter
@Setter
private int failCount;
public PlatformKeepaliveTask(String platformServerId, long delayTime, PlatformKeepaliveCallback callback) {
this.platformServerId = platformServerId;
this.delayTime = System.currentTimeMillis() + delayTime;
this.callback = callback;
}
public void expired() {
if (callback == null) {
log.info("[平台心跳到期] 未找到到期处理回调, 平台上级编号: {}", platformServerId);
return;
}
getCallback().run(platformServerId, failCount);
}
@Override
public long getDelay(@NotNull TimeUnit unit) {
return unit.convert(delayTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(@NotNull Delayed o) {
return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
}
}

View File

@ -0,0 +1,70 @@
package com.genersoft.iot.vmp.gb28181.task.platformStatus;
import com.genersoft.iot.vmp.common.CommonCallback;
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
* 平台注册任务
*/
@Slf4j
public class PlatformRegisterTask implements Delayed {
@Getter
private String platformServerId;
/**
* 超时时间(单位 毫秒)
*/
@Getter
@Setter
private long delayTime;
@Getter
private SipTransactionInfo sipTransactionInfo;
/**
* 到期回调
*/
@Getter
private CommonCallback<String> callback;
public PlatformRegisterTask(String platformServerId, long delayTime, SipTransactionInfo sipTransactionInfo, CommonCallback<String> callback) {
this.platformServerId = platformServerId;
this.delayTime = System.currentTimeMillis() + delayTime;
this.callback = callback;
this.sipTransactionInfo = sipTransactionInfo;
}
public void expired() {
if (callback == null) {
log.info("[平台注册到期] 未找到到期处理回调, 平台上级编号: {}", platformServerId);
return;
}
getCallback().run(platformServerId);
}
@Override
public long getDelay(@NotNull TimeUnit unit) {
return unit.convert(delayTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(@NotNull Delayed o) {
return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
}
public PlatformRegisterTaskInfo getInfo() {
PlatformRegisterTaskInfo taskInfo = new PlatformRegisterTaskInfo();
taskInfo.setPlatformServerId(platformServerId);
taskInfo.setSipTransactionInfo(sipTransactionInfo);
return taskInfo;
}
}

View File

@ -0,0 +1,29 @@
package com.genersoft.iot.vmp.gb28181.task.platformStatus;
import com.genersoft.iot.vmp.common.CommonCallback;
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
* 平台注册任务可序列化的信息
*/
@Slf4j
@Data
public class PlatformRegisterTaskInfo{
private String platformServerId;
private SipTransactionInfo sipTransactionInfo;
/**
* 过期时间
*/
private long expireTime;
}

View File

@ -0,0 +1,202 @@
package com.genersoft.iot.vmp.gb28181.task.platformStatus;
import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import com.genersoft.iot.vmp.utils.redis.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class PlatformStatusTaskRunner {
private final Map<String, PlatformRegisterTask> registerSubscribes = new ConcurrentHashMap<>();
private final DelayQueue<PlatformRegisterTask> registerDelayQueue = new DelayQueue<>();
private final Map<String, PlatformKeepaliveTask> keepaliveSubscribes = new ConcurrentHashMap<>();
private final DelayQueue<PlatformKeepaliveTask> keepaliveTaskDelayQueue = new DelayQueue<>();
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
@Autowired
private UserSetting userSetting;
private final String prefix = "VMP_PLATFORM_STATUS";
// 订阅过期检查
@Scheduled(fixedDelay = 500, timeUnit = TimeUnit.MILLISECONDS)
public void expirationCheckForRegister(){
while (!registerDelayQueue.isEmpty()) {
PlatformRegisterTask take = null;
try {
take = registerDelayQueue.take();
try {
removeRegisterTask(take.getPlatformServerId());
take.expired();
}catch (Exception e) {
log.error("[平台注册到期] 到期处理时出现异常, 平台上级编号: {} ", take.getPlatformServerId());
}
} catch (InterruptedException e) {
log.error("[平台注册到期] ", e);
}
}
}
@Scheduled(fixedDelay = 500, timeUnit = TimeUnit.MILLISECONDS)
public void expirationCheckForKeepalive(){
while (!keepaliveTaskDelayQueue.isEmpty()) {
PlatformKeepaliveTask take = null;
try {
take = keepaliveTaskDelayQueue.take();
try {
removeKeepAliveTask(take.getPlatformServerId());
take.expired();
}catch (Exception e) {
log.error("[平台心跳到期] 到期处理时出现异常, 平台上级编号: {} ", take.getPlatformServerId());
}
} catch (InterruptedException e) {
log.error("[平台心跳到期] ", e);
}
}
}
public void addRegisterTask(PlatformRegisterTask task) {
Duration duration = Duration.ofSeconds((task.getDelayTime() - System.currentTimeMillis())/1000);
if (duration.getSeconds() < 0) {
return;
}
registerSubscribes.put(task.getPlatformServerId(), task);
String key = String.format("%s_%s_%s", prefix, userSetting.getServerId(), task.getPlatformServerId());
redisTemplate.opsForValue().set(key, task.getInfo(), duration);
registerDelayQueue.offer(task);
}
public boolean removeRegisterTask(String platformServerId) {
PlatformRegisterTask task = registerSubscribes.get(platformServerId);
if (task != null) {
registerSubscribes.remove(platformServerId);
}
String redisKey = String.format("%s_%s_%s", prefix, userSetting.getServerId(), platformServerId);
redisTemplate.delete(redisKey);
if (registerDelayQueue.contains(task)) {
boolean remove = registerDelayQueue.remove(task);
if (!remove) {
log.info("[移除平台注册任务] 从延时队列内移除失败: {}", platformServerId);
}
}
return true;
}
public SipTransactionInfo getRegisterTransactionInfo(String platformServerId) {
PlatformRegisterTask task = registerSubscribes.get(platformServerId);
if (task == null) {
return null;
}
return task.getSipTransactionInfo();
}
public boolean updateRegisterDelay(String platformServerId, long expirationTime) {
PlatformRegisterTask task = registerSubscribes.get(platformServerId);
if (task == null) {
return false;
}
log.info("[更新平台注册任务时间] 平台上级编号: {}", platformServerId);
if (registerDelayQueue.contains(task)) {
boolean remove = registerDelayQueue.remove(task);
if (!remove) {
log.info("[更新平台注册任务时间] 从延时队列内移除失败: {}", platformServerId);
}
}
task.setDelayTime(expirationTime);
registerDelayQueue.offer(task);
String redisKey = String.format("%s_%s_%s", prefix, userSetting.getServerId(), platformServerId);
Duration duration = Duration.ofSeconds((expirationTime - System.currentTimeMillis())/1000);
redisTemplate.expire(redisKey, duration);
return true;
}
public boolean containsRegister(String platformServerId) {
return registerSubscribes.containsKey(platformServerId);
}
public List<PlatformRegisterTaskInfo> getAllRegisterTaskInfo(){
return getRegisterTransactionInfoByServerId(userSetting.getServerId());
}
public void addKeepAliveTask(PlatformKeepaliveTask task) {
Duration duration = Duration.ofSeconds((task.getDelayTime() - System.currentTimeMillis())/1000);
if (duration.getSeconds() < 0) {
return;
}
keepaliveSubscribes.put(task.getPlatformServerId(), task);
keepaliveTaskDelayQueue.offer(task);
}
public boolean removeKeepAliveTask(String platformServerId) {
PlatformKeepaliveTask task = keepaliveSubscribes.get(platformServerId);
if (task != null) {
keepaliveSubscribes.remove(platformServerId);
}
if (keepaliveTaskDelayQueue.contains(task)) {
boolean remove = keepaliveTaskDelayQueue.remove(task);
if (!remove) {
log.info("[移除平台心跳任务] 从延时队列内移除失败: {}", platformServerId);
}
}
return true;
}
public boolean updateKeepAliveDelay(String platformServerId, long expirationTime) {
PlatformKeepaliveTask task = keepaliveSubscribes.get(platformServerId);
if (task == null) {
return false;
}
log.info("[更新平台心跳任务时间] 平台上级编号: {}", platformServerId);
if (keepaliveTaskDelayQueue.contains(task)) {
boolean remove = keepaliveTaskDelayQueue.remove(task);
if (!remove) {
log.info("[更新平台心跳任务时间] 从延时队列内移除失败: {}", platformServerId);
}
}
task.setDelayTime(expirationTime);
keepaliveTaskDelayQueue.offer(task);
return true;
}
public boolean containsKeepAlive(String platformServerId) {
return keepaliveSubscribes.containsKey(platformServerId);
}
public List<PlatformRegisterTaskInfo> getRegisterTransactionInfoByServerId(String serverId) {
String scanKey = String.format("%s_%s_*", prefix, serverId);
List<Object> values = RedisUtil.scan(redisTemplate, scanKey);
if (values.isEmpty()) {
return new ArrayList<>();
}
List<PlatformRegisterTaskInfo> result = new ArrayList<>();
for (Object value : values) {
String redisKey = (String)value;
PlatformRegisterTaskInfo taskInfo = (PlatformRegisterTaskInfo)redisTemplate.opsForValue().get(redisKey);
if (taskInfo == null) {
continue;
}
Long expire = redisTemplate.getExpire(redisKey);
taskInfo.setExpireTime(expire);
result.add(taskInfo);
}
return result;
}
}

View File

@ -84,7 +84,11 @@ public class SIPProcessorObserver implements ISIPProcessorObserver {
// Success // Success
if (((status >= Response.OK) && (status < Response.MULTIPLE_CHOICES)) || status == Response.UNAUTHORIZED) { if (((status >= Response.OK) && (status < Response.MULTIPLE_CHOICES)) || status == Response.UNAUTHORIZED) {
if (status != Response.UNAUTHORIZED && responseEvent.getResponse() != null && !sipSubscribe.isEmpty() ) { ISIPResponseProcessor sipRequestProcessor = responseProcessorMap.get(response.getCSeqHeader().getMethod());
if (sipRequestProcessor != null) {
sipRequestProcessor.process(responseEvent);
}
CallIdHeader callIdHeader = response.getCallIdHeader(); CallIdHeader callIdHeader = response.getCallIdHeader();
CSeqHeader cSeqHeader = response.getCSeqHeader(); CSeqHeader cSeqHeader = response.getCSeqHeader();
if (callIdHeader != null) { if (callIdHeader != null) {
@ -97,11 +101,6 @@ public class SIPProcessorObserver implements ISIPProcessorObserver {
sipSubscribe.removeSubscribe(callIdHeader.getCallId() + cSeqHeader.getSeqNumber()); sipSubscribe.removeSubscribe(callIdHeader.getCallId() + cSeqHeader.getSeqNumber());
} }
} }
}
ISIPResponseProcessor sipRequestProcessor = responseProcessorMap.get(response.getCSeqHeader().getMethod());
if (sipRequestProcessor != null) {
sipRequestProcessor.process(responseEvent);
}
} else if ((status >= Response.TRYING) && (status < Response.OK)) { } else if ((status >= Response.TRYING) && (status < Response.OK)) {
// 增加其它无需回复的响应如101180等 // 增加其它无需回复的响应如101180等
// 更新sip订阅的时间 // 更新sip订阅的时间

View File

@ -2,21 +2,22 @@ package com.genersoft.iot.vmp.gb28181.transmit;
import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.conf.SipConfig;
import com.genersoft.iot.vmp.gb28181.SipLayer; import com.genersoft.iot.vmp.gb28181.SipLayer;
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe; import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.gb28181.event.sip.SipEvent; import com.genersoft.iot.vmp.gb28181.event.sip.SipEvent;
import com.genersoft.iot.vmp.gb28181.utils.SipUtils; import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
import com.genersoft.iot.vmp.utils.GitUtil; import com.genersoft.iot.vmp.utils.GitUtil;
import gov.nist.javax.sip.SipProviderImpl; import gov.nist.javax.sip.SipProviderImpl;
import gov.nist.javax.sip.address.SipUri;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.message.SIPResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
import javax.sip.SipException; import javax.sip.SipException;
import javax.sip.header.CSeqHeader; import javax.sip.header.*;
import javax.sip.header.CallIdHeader;
import javax.sip.header.UserAgentHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.Message; import javax.sip.message.Message;
import javax.sip.message.Request; import javax.sip.message.Request;
import javax.sip.message.Response; import javax.sip.message.Response;
@ -39,6 +40,7 @@ public class SIPSender {
@Autowired @Autowired
private SipSubscribe sipSubscribe; private SipSubscribe sipSubscribe;
@Autowired @Autowired
private SipConfig sipConfig; private SipConfig sipConfig;
@ -73,6 +75,7 @@ public class SIPSender {
if (okEvent != null || errorEvent != null) { if (okEvent != null || errorEvent != null) {
CallIdHeader callIdHeader = (CallIdHeader) message.getHeader(CallIdHeader.NAME); CallIdHeader callIdHeader = (CallIdHeader) message.getHeader(CallIdHeader.NAME);
CSeqHeader cSeqHeader = (CSeqHeader) message.getHeader(CSeqHeader.NAME); CSeqHeader cSeqHeader = (CSeqHeader) message.getHeader(CSeqHeader.NAME);
FromHeader fromHeader = (FromHeader) message.getHeader(FromHeader.NAME);
String key = callIdHeader.getCallId() + cSeqHeader.getSeqNumber(); String key = callIdHeader.getCallId() + cSeqHeader.getSeqNumber();
SipEvent sipEvent = SipEvent.getInstance(key, eventResult -> { SipEvent sipEvent = SipEvent.getInstance(key, eventResult -> {
sipSubscribe.removeSubscribe(key); sipSubscribe.removeSubscribe(key);
@ -85,6 +88,28 @@ public class SIPSender {
errorEvent.response(eventResult); errorEvent.response(eventResult);
} }
}), timeout == null ? sipConfig.getTimeout() : timeout); }), timeout == null ? sipConfig.getTimeout() : timeout);
SipTransactionInfo sipTransactionInfo = new SipTransactionInfo();
sipTransactionInfo.setFromTag(fromHeader.getTag());
sipTransactionInfo.setCallId(callIdHeader.getCallId());
if (message instanceof SIPResponse) {
SIPResponse response = (SIPResponse) message;
sipTransactionInfo.setToTag(response.getToHeader().getTag());
sipTransactionInfo.setViaBranch(response.getTopmostViaHeader().getBranch());
}else if (message instanceof SIPRequest) {
SIPRequest request = (SIPRequest) message;
sipTransactionInfo.setViaBranch(request.getTopmostViaHeader().getBranch());
SipUri sipUri = (SipUri)request.getRequestLine().getUri();
sipTransactionInfo.setUser(sipUri.getUser());
}
ExpiresHeader expiresHeader = (ExpiresHeader) message.getHeader(ExpiresHeader.NAME);
if (expiresHeader != null) {
sipTransactionInfo.setExpires(expiresHeader.getExpires());
}
sipEvent.setSipTransactionInfo(sipTransactionInfo);
sipSubscribe.addSubscribe(key, sipEvent); sipSubscribe.addSubscribe(key, sipEvent);
} }

View File

@ -271,7 +271,7 @@ public interface ISIPCommander {
* @param device 视频设备 * @param device 视频设备
* @return true = 命令发送成功 * @return true = 命令发送成功
*/ */
SIPRequest mobilePositionSubscribe(Device device, SIPRequest request, SipSubscribe.Event okEvent , SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; SIPRequest mobilePositionSubscribe(Device device, SipTransactionInfo transactionInfo, SipSubscribe.Event okEvent , SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
/** /**
* 订阅取消订阅报警信息 * 订阅取消订阅报警信息
@ -290,7 +290,7 @@ public interface ISIPCommander {
* @param device 视频设备 * @param device 视频设备
* @return true = 命令发送成功 * @return true = 命令发送成功
*/ */
SIPRequest catalogSubscribe(Device device, SIPRequest request, SipSubscribe.Event okEvent ,SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException; SIPRequest catalogSubscribe(Device device, SipTransactionInfo transactionInfo, SipSubscribe.Event okEvent ,SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
/** /**
* 拉框控制命令 * 拉框控制命令

View File

@ -225,11 +225,11 @@ public class SIPRequestHeaderPlarformProvider {
SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(),
parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort()); parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort());
Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, subscribeInfo.getResponse() != null ? subscribeInfo.getResponse().getToTag(): subscribeInfo.getSimulatedToTag()); FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, subscribeInfo.getTransactionInfo() != null ? subscribeInfo.getTransactionInfo() .getToTag(): subscribeInfo.getSimulatedToTag());
// to // to
SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerGBDomain()); SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerGBDomain());
Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, subscribeInfo.getRequest() != null ?subscribeInfo.getRequest().getFromTag(): subscribeInfo.getSimulatedFromTag()); ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, subscribeInfo.getTransactionInfo() != null ?subscribeInfo.getTransactionInfo().getFromTag(): subscribeInfo.getSimulatedFromTag());
// Forwards // Forwards
MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);
@ -239,7 +239,7 @@ public class SIPRequestHeaderPlarformProvider {
// 设置编码 防止中文乱码 // 设置编码 防止中文乱码
messageFactory.setDefaultContentEncodingCharset("gb2312"); messageFactory.setDefaultContentEncodingCharset("gb2312");
CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(subscribeInfo.getRequest() != null ? subscribeInfo.getRequest().getCallIdHeader().getCallId(): subscribeInfo.getSimulatedCallId()); CallIdHeader callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(subscribeInfo.getTransactionInfo() != null ? subscribeInfo.getTransactionInfo().getCallId(): subscribeInfo.getSimulatedCallId());
request = (SIPRequest) messageFactory.createRequest(requestURI, Request.NOTIFY, callIdHeader, cSeqHeader, fromHeader, request = (SIPRequest) messageFactory.createRequest(requestURI, Request.NOTIFY, callIdHeader, cSeqHeader, fromHeader,
toHeader, viaHeaders, maxForwards); toHeader, viaHeaders, maxForwards);

View File

@ -231,7 +231,7 @@ public class SIPRequestHeaderProvider {
return request; return request;
} }
public Request createSubscribeRequest(Device device, String content, SIPRequest requestOld, Integer expires, String event, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException { public Request createSubscribeRequest(Device device, String content, SipTransactionInfo sipTransactionInfo, Integer expires, String event, CallIdHeader callIdHeader) throws ParseException, InvalidArgumentException, PeerUnavailableException {
Request request = null; Request request = null;
// sipuri // sipuri
SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); SipURI requestURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
@ -244,11 +244,11 @@ public class SIPRequestHeaderProvider {
// from // from
SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain()); SipURI fromSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(), sipConfig.getDomain());
Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI); Address fromAddress = SipFactory.getInstance().createAddressFactory().createAddress(fromSipURI);
FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, requestOld == null ? SipUtils.getNewFromTag() :requestOld.getFromTag()); FromHeader fromHeader = SipFactory.getInstance().createHeaderFactory().createFromHeader(fromAddress, sipTransactionInfo == null ? SipUtils.getNewFromTag() :sipTransactionInfo.getFromTag());
// to // to
SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress()); SipURI toSipURI = SipFactory.getInstance().createAddressFactory().createSipURI(device.getDeviceId(), device.getHostAddress());
Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI); Address toAddress = SipFactory.getInstance().createAddressFactory().createAddress(toSipURI);
ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, requestOld == null ? null :requestOld.getToTag()); ToHeader toHeader = SipFactory.getInstance().createHeaderFactory().createToHeader(toAddress, sipTransactionInfo == null ? null :sipTransactionInfo.getToTag());
// Forwards // Forwards
MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70); MaxForwardsHeader maxForwards = SipFactory.getInstance().createHeaderFactory().createMaxForwardsHeader(70);

View File

@ -1106,7 +1106,9 @@ public class SIPCommander implements ISIPCommander {
Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport())); Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, eventResult -> { sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, eventResult -> {
messageSubscribe.removeSubscribe(messageEvent.getKey()); messageSubscribe.removeSubscribe(messageEvent.getKey());
if (callback != null) {
callback.run(ErrorCode.ERROR100.getCode(), "失败," + eventResult.msg, null); callback.run(ErrorCode.ERROR100.getCode(), "失败," + eventResult.msg, null);
}
}); });
} }
@ -1177,7 +1179,7 @@ public class SIPCommander implements ISIPCommander {
* @return true = 命令发送成功 * @return true = 命令发送成功
*/ */
@Override @Override
public SIPRequest mobilePositionSubscribe(Device device, SIPRequest requestOld, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { public SIPRequest mobilePositionSubscribe(Device device, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
StringBuffer subscribePostitionXml = new StringBuffer(200); StringBuffer subscribePostitionXml = new StringBuffer(200);
String charset = device.getCharset(); String charset = device.getCharset();
@ -1195,12 +1197,12 @@ public class SIPCommander implements ISIPCommander {
CallIdHeader callIdHeader; CallIdHeader callIdHeader;
if (requestOld != null) { if (sipTransactionInfo != null) {
callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId()); callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(sipTransactionInfo.getCallId());
} else { } else {
callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()); callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport());
} }
SIPRequest request = (SIPRequest) headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), requestOld, device.getSubscribeCycleForMobilePosition(), "presence",callIdHeader); //Position;id=" + tm.substring(tm.length() - 4)); SIPRequest request = (SIPRequest) headerProvider.createSubscribeRequest(device, subscribePostitionXml.toString(), sipTransactionInfo, device.getSubscribeCycleForMobilePosition(), "presence",callIdHeader); //Position;id=" + tm.substring(tm.length() - 4));
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent); sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent);
return request; return request;
@ -1253,7 +1255,7 @@ public class SIPCommander implements ISIPCommander {
} }
@Override @Override
public SIPRequest catalogSubscribe(Device device, SIPRequest requestOld, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException { public SIPRequest catalogSubscribe(Device device, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
StringBuffer cmdXml = new StringBuffer(200); StringBuffer cmdXml = new StringBuffer(200);
String charset = device.getCharset(); String charset = device.getCharset();
@ -1266,14 +1268,14 @@ public class SIPCommander implements ISIPCommander {
CallIdHeader callIdHeader; CallIdHeader callIdHeader;
if (requestOld != null) { if (sipTransactionInfo != null) {
callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(requestOld.getCallIdHeader().getCallId()); callIdHeader = SipFactory.getInstance().createHeaderFactory().createCallIdHeader(sipTransactionInfo.getCallId());
} else { } else {
callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()); callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport());
} }
// 有效时间默认为60秒以上 // 有效时间默认为60秒以上
SIPRequest request = (SIPRequest) headerProvider.createSubscribeRequest(device, cmdXml.toString(), requestOld, device.getSubscribeCycleForCatalog(), "Catalog", SIPRequest request = (SIPRequest) headerProvider.createSubscribeRequest(device, cmdXml.toString(), sipTransactionInfo, device.getSubscribeCycleForCatalog(), "Catalog",
callIdHeader); callIdHeader);
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent); sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent);
return request; return request;

View File

@ -21,7 +21,6 @@ import com.genersoft.iot.vmp.media.service.IMediaServerService;
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import com.genersoft.iot.vmp.service.bean.SSRCInfo; import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.utils.DateUtil;
import com.genersoft.iot.vmp.utils.GitUtil; import com.genersoft.iot.vmp.utils.GitUtil;
import gov.nist.javax.sip.message.MessageFactoryImpl; import gov.nist.javax.sip.message.MessageFactoryImpl;
@ -121,9 +120,6 @@ public class SIPCommanderForPlatform implements ISIPCommanderForPlatform {
request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform,
redisCatchStorage.getCSEQ(), fromTag, redisCatchStorage.getCSEQ(), fromTag,
toTag, callIdHeader, isRegister? parentPlatform.getExpires() : 0); toTag, callIdHeader, isRegister? parentPlatform.getExpires() : 0);
// callid 写入缓存 等注册成功可以更新状态
String callIdFromHeader = callIdHeader.getCallId();
redisCatchStorage.updatePlatformRegisterInfo(callIdFromHeader, PlatformRegisterInfo.getInstance(parentPlatform.getServerGBId(), isRegister));
}else { }else {
request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, fromTag, toTag, www, callIdHeader, isRegister? parentPlatform.getExpires() : 0); request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, fromTag, toTag, www, callIdHeader, isRegister? parentPlatform.getExpires() : 0);
} }
@ -132,11 +128,10 @@ public class SIPCommanderForPlatform implements ISIPCommanderForPlatform {
if (event != null) { if (event != null) {
log.info("[国标级联]{}, 注册失败: {} ", parentPlatform.getServerGBId(), event.msg); log.info("[国标级联]{}, 注册失败: {} ", parentPlatform.getServerGBId(), event.msg);
} }
redisCatchStorage.delPlatformRegisterInfo(callIdHeader.getCallId());
if (errorEvent != null ) { if (errorEvent != null ) {
errorEvent.response(event); errorEvent.response(event);
} }
}, okEvent, 5L); }, okEvent, 2000L);
} }
@Override @Override
@ -369,7 +364,7 @@ public class SIPCommanderForPlatform implements ISIPCommanderForPlatform {
.append("<CmdType>MobilePosition</CmdType>\r\n") .append("<CmdType>MobilePosition</CmdType>\r\n")
.append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n") .append("<SN>" + (int)((Math.random()*9+1)*100000) + "</SN>\r\n")
.append("<DeviceID>" + channel.getGbDeviceId() + "</DeviceID>\r\n") .append("<DeviceID>" + channel.getGbDeviceId() + "</DeviceID>\r\n")
.append("<Time>" + gpsMsgInfo.getTime() + "</Time>\r\n") .append("<Time>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(gpsMsgInfo.getTime()) + "</Time>\r\n")
.append("<Longitude>" + gpsMsgInfo.getLng() + "</Longitude>\r\n") .append("<Longitude>" + gpsMsgInfo.getLng() + "</Longitude>\r\n")
.append("<Latitude>" + gpsMsgInfo.getLat() + "</Latitude>\r\n") .append("<Latitude>" + gpsMsgInfo.getLat() + "</Latitude>\r\n")
.append("<Speed>" + gpsMsgInfo.getSpeed() + "</Speed>\r\n") .append("<Speed>" + gpsMsgInfo.getSpeed() + "</Speed>\r\n")

View File

@ -143,6 +143,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
mediaServerService.releaseSsrc(mediaInfo.getId(), sendRtpItem.getSsrc()); mediaServerService.releaseSsrc(mediaInfo.getId(), sendRtpItem.getSsrc());
} }
} }
if (sendRtpItem.getServerId().equals(userSetting.getServerId())) {
MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId()); MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId());
if (mediaServer != null) { if (mediaServer != null) {
AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getChannelId()); AudioBroadcastCatch audioBroadcastCatch = audioBroadcastManager.get(sendRtpItem.getChannelId());
@ -154,7 +155,7 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
MediaInfo mediaInfo = mediaServerService.getMediaInfo(mediaServer, sendRtpItem.getApp(), streamId); MediaInfo mediaInfo = mediaServerService.getMediaInfo(mediaServer, sendRtpItem.getApp(), streamId);
if (mediaInfo.getReaderCount() <= 0) { if (mediaInfo != null && mediaInfo.getReaderCount() <= 0) {
log.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId); log.info("[收到bye] {} 无其它观看者,通知设备停止推流", streamId);
if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) { if (sendRtpItem.getPlayType().equals(InviteStreamType.PLAY)) {
Device device = deviceService.getDeviceByDeviceId(sendRtpItem.getTargetId()); Device device = deviceService.getDeviceByDeviceId(sendRtpItem.getTargetId());
@ -177,6 +178,10 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In
} }
} }
} }
} else {
// TODO 流再其他wvp上时应该通知这个wvp停止推流和发送BYE
}
} }
// 可能是设备发送的停止 // 可能是设备发送的停止
SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByCallId(callIdHeader.getCallId()); SsrcTransaction ssrcTransaction = sessionManager.getSsrcTransactionByCallId(callIdHeader.getCallId());

View File

@ -172,11 +172,14 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
// 点播成功 TODO 可以在此处检测cancel命令是否存在存在则不发送 // 点播成功 TODO 可以在此处检测cancel命令是否存在存在则不发送
if (userSetting.getUseCustomSsrcForParentInvite()) { if (userSetting.getUseCustomSsrcForParentInvite()) {
// 上级平台点播时不使用上级平台指定的ssrc使用自定义的ssrc参考国标文档-点播外域设备媒体流SSRC处理方式 // 上级平台点播时不使用上级平台指定的ssrc使用自定义的ssrc参考国标文档-点播外域设备媒体流SSRC处理方式
MediaServer mediaServer = mediaServerService.getOne(streamInfo.getMediaServer().getId());
if (mediaServer != null) {
String ssrc = "Play".equalsIgnoreCase(inviteInfo.getSessionName()) String ssrc = "Play".equalsIgnoreCase(inviteInfo.getSessionName())
? ssrcFactory.getPlaySsrc(streamInfo.getMediaServer().getId()) ? ssrcFactory.getPlaySsrc(streamInfo.getMediaServer().getId())
: ssrcFactory.getPlayBackSsrc(streamInfo.getMediaServer().getId()); : ssrcFactory.getPlayBackSsrc(streamInfo.getMediaServer().getId());
inviteInfo.setSsrc(ssrc); inviteInfo.setSsrc(ssrc);
} }
}
// 构建sendRTP内容 // 构建sendRTP内容
SendRtpInfo sendRtpItem = sendRtpServerService.createSendRtpInfo(streamInfo.getMediaServer(), SendRtpInfo sendRtpItem = sendRtpServerService.createSendRtpInfo(streamInfo.getMediaServer(),
inviteInfo.getIp(), inviteInfo.getPort(), inviteInfo.getSsrc(), platform.getServerGBId(), inviteInfo.getIp(), inviteInfo.getPort(), inviteInfo.getSsrc(), platform.getServerGBId(),
@ -628,8 +631,13 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
request.getCallIdHeader().getCallId(), sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc(), sendRtpItem.getMediaServerId(), sipResponse, InviteSessionType.BROADCAST); request.getCallIdHeader().getCallId(), sendRtpItem.getApp(), sendRtpItem.getStream(), sendRtpItem.getSsrc(), sendRtpItem.getMediaServerId(), sipResponse, InviteSessionType.BROADCAST);
sessionManager.put(ssrcTransaction); sessionManager.put(ssrcTransaction);
// 开启发流大华在收到200OK后就会开始建立连接 // 开启发流大华在收到200OK后就会开始建立连接
if (!device.isBroadcastPushAfterAck()) { if (sendRtpItem.isTcpActive() || !device.isBroadcastPushAfterAck()) {
if (sendRtpItem.isTcpActive()) {
log.info("[语音喊话] 监听端口等待设备连接后推流");
}else {
log.info("[语音喊话] 回复200OK后发现 BroadcastPushAfterAck为False现在开始推流"); log.info("[语音喊话] 回复200OK后发现 BroadcastPushAfterAck为False现在开始推流");
}
playService.startPushStream(sendRtpItem, channel, sipResponse, parentPlatform, request.getCallIdHeader()); playService.startPushStream(sendRtpItem, channel, sipResponse, parentPlatform, request.getCallIdHeader());
} }

View File

@ -5,7 +5,7 @@ import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper; import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.GbSipDate; import com.genersoft.iot.vmp.gb28181.bean.GbSipDate;
import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo; import com.genersoft.iot.vmp.common.RemoteAddressInfo;
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo; import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.gb28181.service.IDeviceService;
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;

View File

@ -1,9 +1,6 @@
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl; package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
import com.genersoft.iot.vmp.gb28181.bean.CmdType; import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.bean.Platform;
import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo;
import com.genersoft.iot.vmp.gb28181.service.IPlatformService; import com.genersoft.iot.vmp.gb28181.service.IPlatformService;
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
@ -23,6 +20,7 @@ import org.springframework.stereotype.Component;
import javax.sip.InvalidArgumentException; import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent; import javax.sip.RequestEvent;
import javax.sip.SipException; import javax.sip.SipException;
import javax.sip.header.EventHeader;
import javax.sip.header.ExpiresHeader; import javax.sip.header.ExpiresHeader;
import javax.sip.message.Response; import javax.sip.message.Response;
import java.text.ParseException; import java.text.ParseException;
@ -72,6 +70,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
return; return;
} }
String cmd = XmlUtil.getText(rootElement, "CmdType"); String cmd = XmlUtil.getText(rootElement, "CmdType");
log.info("[收到订阅请求] 类型: {}", cmd);
if (CmdType.MOBILE_POSITION.equals(cmd)) { if (CmdType.MOBILE_POSITION.equals(cmd)) {
processNotifyMobilePosition(request, rootElement); processNotifyMobilePosition(request, rootElement);
// } else if (CmdType.ALARM.equals(cmd)) { // } else if (CmdType.ALARM.equals(cmd)) {
@ -106,7 +105,6 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
String platformId = SipUtils.getUserIdFromFromHeader(request); String platformId = SipUtils.getUserIdFromFromHeader(request);
String deviceId = XmlUtil.getText(rootElement, "DeviceID"); String deviceId = XmlUtil.getText(rootElement, "DeviceID");
Platform platform = platformService.queryPlatformByServerGBId(platformId); Platform platform = platformService.queryPlatformByServerGBId(platformId);
SubscribeInfo subscribeInfo = new SubscribeInfo(request, platformId);
if (platform == null) { if (platform == null) {
return; return;
} }
@ -122,6 +120,14 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
.append("<Result>OK</Result>\r\n") .append("<Result>OK</Result>\r\n")
.append("</Response>\r\n"); .append("</Response>\r\n");
try {
int expires = request.getExpires().getExpires();
SIPResponse response = responseXmlAck(request, resultXml.toString(), platform, expires);
SubscribeInfo subscribeInfo = SubscribeInfo.getInstance(response, platformId, expires,
(EventHeader)request.getHeader(EventHeader.NAME));
if (subscribeInfo.getExpires() > 0) { if (subscribeInfo.getExpires() > 0) {
// GPS上报时间间隔 // GPS上报时间间隔
String interval = XmlUtil.getText(rootElement, "Interval"); String interval = XmlUtil.getText(rootElement, "Interval");
@ -132,13 +138,10 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
} }
subscribeInfo.setSn(sn); subscribeInfo.setSn(sn);
} }
try {
SIPResponse response = responseXmlAck(request, resultXml.toString(), platform, subscribeInfo.getExpires());
if (subscribeInfo.getExpires() == 0) { if (subscribeInfo.getExpires() == 0) {
subscribeHolder.removeMobilePositionSubscribe(platformId); subscribeHolder.removeMobilePositionSubscribe(platformId);
}else { }else {
subscribeInfo.setResponse(response); subscribeInfo.setTransactionInfo(new SipTransactionInfo(response));
subscribeHolder.putMobilePositionSubscribe(platformId, subscribeInfo, ()->{ subscribeHolder.putMobilePositionSubscribe(platformId, subscribeInfo, ()->{
platformService.sendNotifyMobilePosition(platformId); platformService.sendNotifyMobilePosition(platformId);
}); });
@ -155,15 +158,16 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
private void processNotifyCatalogList(SIPRequest request, Element rootElement) throws SipException { private void processNotifyCatalogList(SIPRequest request, Element rootElement) throws SipException {
if (request == null) { if (request == null) {
log.info("[处理目录订阅] 发现request为NUll。已忽略");
return; return;
} }
String platformId = SipUtils.getUserIdFromFromHeader(request); String platformId = SipUtils.getUserIdFromFromHeader(request);
String deviceId = XmlUtil.getText(rootElement, "DeviceID"); String deviceId = XmlUtil.getText(rootElement, "DeviceID");
Platform platform = platformService.queryPlatformByServerGBId(platformId); Platform platform = platformService.queryPlatformByServerGBId(platformId);
if (platform == null){ if (platform == null){
log.info("[处理目录订阅] 未找到平台 {}。已忽略", platformId);
return; return;
} }
SubscribeInfo subscribeInfo = new SubscribeInfo(request, platformId);
String sn = XmlUtil.getText(rootElement, "SN"); String sn = XmlUtil.getText(rootElement, "SN");
log.info("[回复上级的目录订阅请求]: {}/{}", platformId, deviceId); log.info("[回复上级的目录订阅请求]: {}/{}", platformId, deviceId);
@ -176,18 +180,19 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
.append("<Result>OK</Result>\r\n") .append("<Result>OK</Result>\r\n")
.append("</Response>\r\n"); .append("</Response>\r\n");
if (subscribeInfo.getExpires() > 0) {
subscribeHolder.putCatalogSubscribe(platformId, subscribeInfo);
}else if (subscribeInfo.getExpires() == 0) {
subscribeHolder.removeCatalogSubscribe(platformId);
}
try { try {
int expires = request.getExpires().getExpires();
Platform parentPlatform = platformService.queryPlatformByServerGBId(platformId); Platform parentPlatform = platformService.queryPlatformByServerGBId(platformId);
SIPResponse response = responseXmlAck(request, resultXml.toString(), parentPlatform, subscribeInfo.getExpires()); SIPResponse response = responseXmlAck(request, resultXml.toString(), parentPlatform, expires);
SubscribeInfo subscribeInfo = SubscribeInfo.getInstance(response, platformId, expires,
(EventHeader)request.getHeader(EventHeader.NAME));
if (subscribeInfo.getExpires() == 0) { if (subscribeInfo.getExpires() == 0) {
subscribeHolder.removeCatalogSubscribe(platformId); subscribeHolder.removeCatalogSubscribe(platformId);
}else { }else {
subscribeInfo.setResponse(response); subscribeInfo.setTransactionInfo(new SipTransactionInfo(response));
subscribeHolder.putCatalogSubscribe(platformId, subscribeInfo); subscribeHolder.putCatalogSubscribe(platformId, subscribeInfo);
} }
} catch (SipException | InvalidArgumentException | ParseException e) { } catch (SipException | InvalidArgumentException | ParseException e) {

View File

@ -5,7 +5,7 @@ import com.genersoft.iot.vmp.conf.DynamicTask;
import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.bean.Platform;
import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo; import com.genersoft.iot.vmp.common.RemoteAddressInfo;
import com.genersoft.iot.vmp.gb28181.bean.SipMsgInfo; import com.genersoft.iot.vmp.gb28181.bean.SipMsgInfo;
import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.gb28181.service.IDeviceService;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
@ -97,10 +97,6 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
} }
Device device = sipMsgInfo.getDevice(); Device device = sipMsgInfo.getDevice();
SIPRequest request = (SIPRequest) evt.getRequest(); SIPRequest request = (SIPRequest) evt.getRequest();
// if (!ObjectUtils.isEmpty(device.getKeepaliveTime()) && DateUtil.getDifferenceForNow(device.getKeepaliveTime()) <= 3000L) {
// log.info("[收到心跳] 心跳发送过于频繁,已忽略 device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId());
// return;
// }
RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress()); RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress());
if (!device.getIp().equalsIgnoreCase(remoteAddressInfo.getIp()) || device.getPort() != remoteAddressInfo.getPort()) { if (!device.getIp().equalsIgnoreCase(remoteAddressInfo.getIp()) || device.getPort() != remoteAddressInfo.getPort()) {
@ -109,12 +105,6 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort()))); device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort())));
device.setIp(remoteAddressInfo.getIp()); device.setIp(remoteAddressInfo.getIp());
device.setLocalIp(request.getLocalAddress().getHostAddress()); device.setLocalIp(request.getLocalAddress().getHostAddress());
// 设备地址变化会引起目录订阅任务失效需要重新添加
if (device.getSubscribeCycleForCatalog() > 0) {
deviceService.removeCatalogSubscribe(device, result -> {
deviceService.addCatalogSubscribe(device);
});
}
} }
device.setKeepaliveTime(DateUtil.getNow()); device.setKeepaliveTime(DateUtil.getNow());

View File

@ -78,7 +78,7 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen
String channelId = getText(rootElementAfterCharset, "DeviceID"); String channelId = getText(rootElementAfterCharset, "DeviceID");
DeviceChannel deviceChannel = deviceChannelService.getOne(device.getDeviceId(), channelId); DeviceChannel deviceChannel = deviceChannelService.getOne(device.getDeviceId(), channelId);
if (deviceChannel == null) { if (deviceChannel == null) {
log.warn("[解析报警消息] 未找到通道:{}/{}", device.getDeviceId(), channelId); log.warn("[解析移动位置通知] 未找到通道:{}/{}", device.getDeviceId(), channelId);
continue; continue;
} }

View File

@ -45,7 +45,7 @@ public class ResponseMessageHandler extends MessageHandlerAbstract implements In
String cmd = getText(element, "CmdType"); String cmd = getText(element, "CmdType");
String sn = getText(element, "SN"); String sn = getText(element, "SN");
MessageEvent<Object> subscribe = (MessageEvent<Object>)messageSubscribe.getSubscribe(cmd + sn); MessageEvent<Object> subscribe = (MessageEvent<Object>)messageSubscribe.getSubscribe(cmd + sn);
if (subscribe != null) { if (subscribe != null && subscribe.getCallback() != null) {
String result = getText(element, "Result"); String result = getText(element, "Result");
if (result == null || "OK".equalsIgnoreCase(result) || data != null) { if (result == null || "OK".equalsIgnoreCase(result) || data != null) {
subscribe.getCallback().run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), data); subscribe.getCallback().run(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), data);

View File

@ -1,14 +1,14 @@
package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl; package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
import com.genersoft.iot.vmp.gb28181.bean.Platform; import com.genersoft.iot.vmp.gb28181.bean.Platform;
import com.genersoft.iot.vmp.gb28181.bean.PlatformCatch;
import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo; import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.gb28181.event.sip.SipEvent;
import com.genersoft.iot.vmp.gb28181.service.IPlatformService;
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract; import com.genersoft.iot.vmp.gb28181.transmit.event.response.SIPResponseProcessorAbstract;
import com.genersoft.iot.vmp.gb28181.service.IPlatformService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
import gov.nist.javax.sip.message.SIPResponse; import gov.nist.javax.sip.message.SIPResponse;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -44,6 +44,9 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
@Autowired @Autowired
private IPlatformService platformService; private IPlatformService platformService;
@Autowired
private SipSubscribe sipSubscribe;
@Override @Override
public void afterPropertiesSet() throws Exception { public void afterPropertiesSet() throws Exception {
// 添加消息处理的订阅 // 添加消息处理的订阅
@ -59,23 +62,19 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
public void process(ResponseEvent evt) { public void process(ResponseEvent evt) {
SIPResponse response = (SIPResponse)evt.getResponse(); SIPResponse response = (SIPResponse)evt.getResponse();
String callId = response.getCallIdHeader().getCallId(); String callId = response.getCallIdHeader().getCallId();
PlatformRegisterInfo platformRegisterInfo = redisCatchStorage.queryPlatformRegisterInfo(callId); long seqNumber = response.getCSeqHeader().getSeqNumber();
if (platformRegisterInfo == null) { SipEvent subscribe = sipSubscribe.getSubscribe(callId + seqNumber);
log.info(String.format("[国标级联]未找到callId %s 的注册/注销平台id", callId )); if (subscribe == null || subscribe.getSipTransactionInfo() == null || subscribe.getSipTransactionInfo().getUser() == null) {
return; return;
} }
PlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(platformRegisterInfo.getPlatformId()); String action = subscribe.getSipTransactionInfo().getExpires() > 0 ? "注册" : "注销";
if (parentPlatformCatch == null) { String platFormServerGbId = subscribe.getSipTransactionInfo().getUser();
log.warn(String.format("[国标级联]收到注册/注销%S请求平台%s但是平台缓存信息未查询到!!!", response.getStatusCode(),platformRegisterInfo.getPlatformId()));
return;
}
String action = platformRegisterInfo.isRegister() ? "注册" : "注销"; log.info("[国标级联]{} {}响应 {} ", action, response.getStatusCode(), platFormServerGbId);
log.info(String.format("[国标级联]%s %S响应,%s ", action, response.getStatusCode(), platformRegisterInfo.getPlatformId() )); Platform platform = platformService.queryPlatformByServerGBId(platFormServerGbId);
Platform parentPlatform = parentPlatformCatch.getPlatform(); if (platform == null) {
if (parentPlatform == null) { log.warn("[国标级联]收到 来自{}的 {} 回复 {}, 但是平台信息未查询到!!!", platFormServerGbId, action, response.getStatusCode());
log.warn(String.format("[国标级联]收到 %s %s的%S请求, 但是平台信息未查询到!!!", platformRegisterInfo.getPlatformId(), action, response.getStatusCode()));
return; return;
} }
@ -83,21 +82,17 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
WWWAuthenticateHeader www = (WWWAuthenticateHeader)response.getHeader(WWWAuthenticateHeader.NAME); WWWAuthenticateHeader www = (WWWAuthenticateHeader)response.getHeader(WWWAuthenticateHeader.NAME);
SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(response); SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(response);
try { try {
sipCommanderForPlatform.register(parentPlatform, sipTransactionInfo, www, null, null, platformRegisterInfo.isRegister()); sipCommanderForPlatform.register(platform, sipTransactionInfo, www, null, null, subscribe.getSipTransactionInfo().getExpires() > 0);
} catch (SipException | InvalidArgumentException | ParseException e) { } catch (SipException | InvalidArgumentException | ParseException e) {
log.error("[命令发送失败] 国标级联 再次注册: {}", e.getMessage()); log.error("[命令发送失败] 国标级联 再次注册: {}", e.getMessage());
} }
}else if (response.getStatusCode() == Response.OK){ }else if (response.getStatusCode() == Response.OK){
if (subscribe.getSipTransactionInfo().getExpires() > 0) {
if (platformRegisterInfo.isRegister()) {
SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(response); SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(response);
platformService.online(parentPlatform, sipTransactionInfo); platformService.online(platform, sipTransactionInfo);
}else { }else {
platformService.offline(parentPlatform, true); platformService.offline(platform);
} }
// 注册/注销成功移除缓存的信息
redisCatchStorage.delPlatformRegisterInfo(callId);
} }
} }

Some files were not shown because too many files have changed in this diff Show More