Commit 14c8e7121ad088372c9d4608f70a2da73b569e37
Merge branch 'master_dev' into 'master'
fix:云台控制传参参数错误 See merge request yunteng/thingskit!353
Showing
57 changed files
with
4091 additions
and
184 deletions
Too many changes to show.
To preserve performance only 57 of 202 files are displayed.
... | ... | @@ -92,6 +92,10 @@ |
92 | 92 | </dependency> |
93 | 93 | <dependency> |
94 | 94 | <groupId>org.thingsboard.common.transport</groupId> |
95 | + <artifactId>gbt28181</artifactId> | |
96 | + </dependency> | |
97 | + <dependency> | |
98 | + <groupId>org.thingsboard.common.transport</groupId> | |
95 | 99 | <artifactId>http</artifactId> |
96 | 100 | </dependency> |
97 | 101 | <dependency> | ... | ... |
1 | +--更新区域表的名称 | |
2 | +update sys_area | |
3 | +set name ='澳门特别行政区' | |
4 | +where code = 820000 | |
5 | + and "level" = 'PROVINCE'; | |
6 | +update sys_area | |
7 | +set name ='香港特别行政区' | |
8 | +where code = 810000 | |
9 | + and "level" = 'PROVINCE'; | |
10 | +UPDATE "public"."sys_area" | |
11 | +SET "name" = '香港特别行政区', | |
12 | + "level" = 'CITY' | |
13 | +WHERE "code" = 810000 | |
14 | + AND "parent_id" = 810000; | |
15 | + | |
16 | +UPDATE "public"."sys_area" | |
17 | +SET "name" = '澳门特别行政区', | |
18 | + "level" = 'CITY' | |
19 | +WHERE "code" = 820000 | |
20 | + AND "parent_id" = 820000; | |
21 | +ALTER TABLE tk_device_camera ALTER COLUMN name TYPE varchar(128); | |
22 | +DROP TABLE IF EXISTS "public"."tk_media_server"; | |
23 | +CREATE TABLE "public"."tk_media_server" | |
24 | +( | |
25 | + "id" varchar(36) COLLATE "pg_catalog"."default" NOT NULL, | |
26 | + "ip" varchar(20) COLLATE "pg_catalog"."default", | |
27 | + "hook_ip" varchar(20) COLLATE "pg_catalog"."default", | |
28 | + "sdp_ip" varchar(20) COLLATE "pg_catalog"."default", | |
29 | + "stream_ip" varchar(20) COLLATE "pg_catalog"."default", | |
30 | + "http_port" int4, | |
31 | + "http_ssl_port" int4, | |
32 | + "rtmp_port" int4, | |
33 | + "rtmp_ssl_port" int4, | |
34 | + "rtp_proxy_port" int4, | |
35 | + "rtsp_port" int4, | |
36 | + "rtsp_ssl_port" int4, | |
37 | + "secret" varchar(50) COLLATE "pg_catalog"."default", | |
38 | + "rtp_enable" bool, | |
39 | + "rtp_port_range" varchar(20) COLLATE "pg_catalog"."default", | |
40 | + "send_rtp_port_range" varchar(20) COLLATE "pg_catalog"."default", | |
41 | + "record_assist_port" int4, | |
42 | + "status" int2, | |
43 | + "tenant_id" varchar(36) COLLATE "pg_catalog"."default", | |
44 | + "creator" char(36) COLLATE "pg_catalog"."default", | |
45 | + "create_time" timestamp(6), | |
46 | + "updater" char(36) COLLATE "pg_catalog"."default", | |
47 | + "update_time" timestamp(6), | |
48 | + "remark" varchar(255) COLLATE "pg_catalog"."default", | |
49 | + "default_server" bool, | |
50 | + "media_server_id" varchar(36) COLLATE "pg_catalog"."default", | |
51 | + "auto_config" bool, | |
52 | + "hook_alive_interval" float4 | |
53 | +) | |
54 | +; | |
55 | +COMMENT | |
56 | +ON COLUMN "public"."tk_media_server"."id" IS '主键ID'; | |
57 | +COMMENT | |
58 | +ON COLUMN "public"."tk_media_server"."ip" IS 'IP'; | |
59 | +COMMENT | |
60 | +ON COLUMN "public"."tk_media_server"."hook_ip" IS 'zlmediakit访问平台的IP'; | |
61 | +COMMENT | |
62 | +ON COLUMN "public"."tk_media_server"."sdp_ip" IS 'SDP IP'; | |
63 | +COMMENT | |
64 | +ON COLUMN "public"."tk_media_server"."stream_ip" IS '流IP'; | |
65 | +COMMENT | |
66 | +ON COLUMN "public"."tk_media_server"."http_port" IS 'zlmediakit的HTTP端口'; | |
67 | +COMMENT | |
68 | +ON COLUMN "public"."tk_media_server"."http_ssl_port" IS 'zlmediakit的HTTPS端口'; | |
69 | +COMMENT | |
70 | +ON COLUMN "public"."tk_media_server"."rtmp_port" IS 'zlmediakit的RTMP端口'; | |
71 | +COMMENT | |
72 | +ON COLUMN "public"."tk_media_server"."rtmp_ssl_port" IS 'zlmediakit的RTMPS端口'; | |
73 | +COMMENT | |
74 | +ON COLUMN "public"."tk_media_server"."rtp_proxy_port" IS 'RTP收流端口(单端口模式有用)'; | |
75 | +COMMENT | |
76 | +ON COLUMN "public"."tk_media_server"."rtsp_port" IS 'zlmediakit的RTSP端口'; | |
77 | +COMMENT | |
78 | +ON COLUMN "public"."tk_media_server"."rtsp_ssl_port" IS 'zlmediakit的RTSPS端口'; | |
79 | +COMMENT | |
80 | +ON COLUMN "public"."tk_media_server"."secret" IS 'zlmediakit的鉴权参数'; | |
81 | +COMMENT | |
82 | +ON COLUMN "public"."tk_media_server"."rtp_enable" IS '是否使用多端口模式'; | |
83 | +COMMENT | |
84 | +ON COLUMN "public"."tk_media_server"."rtp_port_range" IS '多端口RTP收流端口范围'; | |
85 | +COMMENT | |
86 | +ON COLUMN "public"."tk_media_server"."send_rtp_port_range" IS '多端口RTP发流端口范围'; | |
87 | +COMMENT | |
88 | +ON COLUMN "public"."tk_media_server"."record_assist_port" IS '录像辅助端口:0代表未使用'; | |
89 | +COMMENT | |
90 | +ON COLUMN "public"."tk_media_server"."status" IS '流媒体服务状态:0离线 1在线'; | |
91 | +COMMENT | |
92 | +ON COLUMN "public"."tk_media_server"."tenant_id" IS '租户ID'; | |
93 | +COMMENT | |
94 | +ON COLUMN "public"."tk_media_server"."creator" IS '创建用户'; | |
95 | +COMMENT | |
96 | +ON COLUMN "public"."tk_media_server"."create_time" IS '创建时间'; | |
97 | +COMMENT | |
98 | +ON COLUMN "public"."tk_media_server"."updater" IS '更新用户'; | |
99 | +COMMENT | |
100 | +ON COLUMN "public"."tk_media_server"."update_time" IS '更新时间'; | |
101 | +COMMENT | |
102 | +ON COLUMN "public"."tk_media_server"."remark" IS '备注'; | |
103 | +COMMENT | |
104 | +ON COLUMN "public"."tk_media_server"."default_server" IS '是否系统默认服务'; | |
105 | +COMMENT | |
106 | +ON COLUMN "public"."tk_media_server"."media_server_id" IS '流媒体ID'; | |
107 | +COMMENT | |
108 | +ON COLUMN "public"."tk_media_server"."auto_config" IS '是否开启自动配置ZLM'; | |
109 | +COMMENT | |
110 | +ON COLUMN "public"."tk_media_server"."hook_alive_interval" IS 'keepalive hook触发间隔,单位秒'; | |
111 | +COMMENT | |
112 | +ON TABLE "public"."tk_media_server" IS '流媒体表(ZLMediakit)GBT28181'; | |
113 | + | |
114 | +-- ---------------------------- | |
115 | +-- Primary Key structure for table tk_media_server | |
116 | +-- ---------------------------- | |
117 | +ALTER TABLE "public"."tk_media_server" | |
118 | + ADD CONSTRAINT "tk_media_server_pkey" PRIMARY KEY ("id"); | |
119 | + | |
120 | + | |
121 | +DROP TABLE IF EXISTS "public"."tk_video_channel"; | |
122 | +CREATE TABLE "public"."tk_video_channel" | |
123 | +( | |
124 | + "id" varchar(36) COLLATE "pg_catalog"."default" NOT NULL, | |
125 | + "name" varchar(36) COLLATE "pg_catalog"."default", | |
126 | + "camera_code" varchar(50) COLLATE "pg_catalog"."default", | |
127 | + "manufacturer" varchar(50) COLLATE "pg_catalog"."default", | |
128 | + "model" varchar(36) COLLATE "pg_catalog"."default", | |
129 | + "owner" varchar(36) COLLATE "pg_catalog"."default", | |
130 | + "civil_code" varchar(50) COLLATE "pg_catalog"."default", | |
131 | + "address" varchar(255) COLLATE "pg_catalog"."default", | |
132 | + "parental" int2, | |
133 | + "safety_way" int2 DEFAULT 0, | |
134 | + "register_way" int2 DEFAULT 1, | |
135 | + "cert_num" varchar(50) COLLATE "pg_catalog"."default", | |
136 | + "certifiable" int2 DEFAULT 0, | |
137 | + "error_code" int2 DEFAULT 1, | |
138 | + "end_time" timestamp(6), | |
139 | + "secrecy" int2 DEFAULT 0, | |
140 | + "ip_address" varchar(36) COLLATE "pg_catalog"."default", | |
141 | + "port" int4, | |
142 | + "password" varchar(128) COLLATE "pg_catalog"."default", | |
143 | + "status" varchar(20) COLLATE "pg_catalog"."default", | |
144 | + "tenant_id" varchar(36) COLLATE "pg_catalog"."default" NOT NULL, | |
145 | + "creator" char(36) COLLATE "pg_catalog"."default", | |
146 | + "create_time" timestamp(6), | |
147 | + "updater" char(36) COLLATE "pg_catalog"."default", | |
148 | + "update_time" timestamp(6), | |
149 | + "remark" varchar(255) COLLATE "pg_catalog"."default", | |
150 | + "channel_id" varchar(50) COLLATE "pg_catalog"."default", | |
151 | + "stream_id" varchar(50) COLLATE "pg_catalog"."default", | |
152 | + "device_id" varchar(36) COLLATE "pg_catalog"."default" | |
153 | +) | |
154 | +; | |
155 | +COMMENT | |
156 | +ON COLUMN "public"."tk_video_channel"."id" IS '视频通道编码ID'; | |
157 | +COMMENT | |
158 | +ON COLUMN "public"."tk_video_channel"."name" IS '视频通道名称'; | |
159 | +COMMENT | |
160 | +ON COLUMN "public"."tk_video_channel"."camera_code" IS '设备国标编号'; | |
161 | +COMMENT | |
162 | +ON COLUMN "public"."tk_video_channel"."manufacturer" IS '制造厂商'; | |
163 | +COMMENT | |
164 | +ON COLUMN "public"."tk_video_channel"."model" IS '型号'; | |
165 | +COMMENT | |
166 | +ON COLUMN "public"."tk_video_channel"."owner" IS '设备归属'; | |
167 | +COMMENT | |
168 | +ON COLUMN "public"."tk_video_channel"."civil_code" IS '行政区域'; | |
169 | +COMMENT | |
170 | +ON COLUMN "public"."tk_video_channel"."address" IS '安装地址'; | |
171 | +COMMENT | |
172 | +ON COLUMN "public"."tk_video_channel"."parental" IS '是否有子设备: 0没有 1有'; | |
173 | +COMMENT | |
174 | +ON COLUMN "public"."tk_video_channel"."safety_way" IS '信令安全模式缺省为0; 0:不采用;2:S/MIME 签名方式;3:S/ | |
175 | +MIME加密签名同时采用方式;4:数字摘要方式'; | |
176 | +COMMENT | |
177 | +ON COLUMN "public"."tk_video_channel"."register_way" IS '注册方式缺省为1;1:符合IETFRFC3261标准的认证注册模 | |
178 | +式;2:基于口令的双向认证注册模式;3:基于数字证书的双向认证注册模式'; | |
179 | +COMMENT | |
180 | +ON COLUMN "public"."tk_video_channel"."cert_num" IS '证书序列号'; | |
181 | +COMMENT | |
182 | +ON COLUMN "public"."tk_video_channel"."certifiable" IS '证书有效标识(有证书的设备必选)缺省为0;证书有效标识:0:无效 1: | |
183 | +有效'; | |
184 | +COMMENT | |
185 | +ON COLUMN "public"."tk_video_channel"."error_code" IS '无效原因码'; | |
186 | +COMMENT | |
187 | +ON COLUMN "public"."tk_video_channel"."end_time" IS '证书终止有效期'; | |
188 | +COMMENT | |
189 | +ON COLUMN "public"."tk_video_channel"."secrecy" IS '保密属性缺省为0;0:不涉密,1:涉密'; | |
190 | +COMMENT | |
191 | +ON COLUMN "public"."tk_video_channel"."ip_address" IS '系统IP地址'; | |
192 | +COMMENT | |
193 | +ON COLUMN "public"."tk_video_channel"."port" IS '端口'; | |
194 | +COMMENT | |
195 | +ON COLUMN "public"."tk_video_channel"."password" IS '密码'; | |
196 | +COMMENT | |
197 | +ON COLUMN "public"."tk_video_channel"."status" IS '通道状态:ON 在线 OFF 离线,使用枚举值'; | |
198 | +COMMENT | |
199 | +ON COLUMN "public"."tk_video_channel"."tenant_id" IS '租户ID'; | |
200 | +COMMENT | |
201 | +ON COLUMN "public"."tk_video_channel"."creator" IS '创建人'; | |
202 | +COMMENT | |
203 | +ON COLUMN "public"."tk_video_channel"."create_time" IS '创建时间'; | |
204 | +COMMENT | |
205 | +ON COLUMN "public"."tk_video_channel"."updater" IS '更新人'; | |
206 | +COMMENT | |
207 | +ON COLUMN "public"."tk_video_channel"."update_time" IS '更新时间'; | |
208 | +COMMENT | |
209 | +ON COLUMN "public"."tk_video_channel"."remark" IS '备注'; | |
210 | +COMMENT | |
211 | +ON COLUMN "public"."tk_video_channel"."channel_id" IS '视频通道编码ID'; | |
212 | +COMMENT | |
213 | +ON COLUMN "public"."tk_video_channel"."stream_id" IS '流唯一编号,存在表示正在直播'; | |
214 | +COMMENT | |
215 | +ON COLUMN "public"."tk_video_channel"."device_id" IS '通道所属设备ID'; | |
216 | +COMMENT | |
217 | +ON TABLE "public"."tk_video_channel" IS '摄像头通道信息表(字段来源于GBT28181)'; | |
218 | + | |
219 | +-- ---------------------------- | |
220 | +-- Primary Key structure for table tk_video_channel | |
221 | +-- ---------------------------- | |
222 | +ALTER TABLE "public"."tk_video_channel" | |
223 | + ADD CONSTRAINT "tk_video_channel_pkey" PRIMARY KEY ("id"); | |
224 | + | |
225 | +DROP TABLE IF EXISTS "public"."tk_device_access_information"; | |
226 | +CREATE TABLE "public"."tk_device_access_information" | |
227 | +( | |
228 | + "id" varchar(64) COLLATE "pg_catalog"."default" NOT NULL, | |
229 | + "intranet_ip" varchar(100) COLLATE "pg_catalog"."default", | |
230 | + "intranet_port" varchar(100) COLLATE "pg_catalog"."default", | |
231 | + "outer_net_ip" varchar(100) COLLATE "pg_catalog"."default", | |
232 | + "outer_net_port" varchar(100) COLLATE "pg_catalog"."default", | |
233 | + "device_agreement" varchar(100) COLLATE "pg_catalog"."default", | |
234 | + "sip_extend" varchar COLLATE "pg_catalog"."default", | |
235 | + "creator" char(64) COLLATE "pg_catalog"."default", | |
236 | + "create_time" timestamp(0), | |
237 | + "updater" char(64) COLLATE "pg_catalog"."default", | |
238 | + "update_time" timestamp(0), | |
239 | + "tenant_id" varchar(64) COLLATE "pg_catalog"."default" | |
240 | +) | |
241 | +; | |
242 | +COMMENT | |
243 | +ON COLUMN "public"."tk_device_access_information"."intranet_ip" IS '内网ip'; | |
244 | +COMMENT | |
245 | +ON COLUMN "public"."tk_device_access_information"."intranet_port" IS '内网端口'; | |
246 | +COMMENT | |
247 | +ON COLUMN "public"."tk_device_access_information"."outer_net_ip" IS '外网IP'; | |
248 | +COMMENT | |
249 | +ON COLUMN "public"."tk_device_access_information"."outer_net_port" IS '外网端口'; | |
250 | +COMMENT | |
251 | +ON COLUMN "public"."tk_device_access_information"."device_agreement" IS '设备接入协议'; | |
252 | +COMMENT | |
253 | +ON COLUMN "public"."tk_device_access_information"."sip_extend" IS 'sip扩展信息 serverId服务器id serverRegion服务器域 password'; | |
254 | +COMMENT | |
255 | +ON COLUMN "public"."tk_device_access_information"."creator" IS '创建人'; | |
256 | +COMMENT | |
257 | +ON COLUMN "public"."tk_device_access_information"."create_time" IS '创建时间'; | |
258 | +COMMENT | |
259 | +ON COLUMN "public"."tk_device_access_information"."updater" IS '更新人'; | |
260 | +COMMENT | |
261 | +ON COLUMN "public"."tk_device_access_information"."update_time" IS '更新时间'; | |
262 | +COMMENT | |
263 | +ON COLUMN "public"."tk_device_access_information"."tenant_id" IS '租户id'; | |
264 | +COMMENT | |
265 | +ON TABLE "public"."tk_device_access_information" IS '设备接入信息表'; | |
266 | + | |
267 | +-- ---------------------------- | |
268 | +-- Primary Key structure for table tk_device_access_information | |
269 | +-- ---------------------------- | |
270 | +ALTER TABLE "public"."tk_device_access_information" | |
271 | + ADD CONSTRAINT "tk_device_access_information_pkey" PRIMARY KEY ("id"); | |
272 | + | |
273 | + | |
274 | +INSERT INTO "public"."sys_dict_item"("id", "dict_id", "item_text", "item_value", "description", "sort", "status", "tenant_id", "creator", "create_time", "updater", "update_time") VALUES ('638bdb47-81bd-4982-bcf7-54ef6239f697', '73d5d8b1-0ac8-475e-97f7-709dd4125ccd', '个人中心', 'system:personal_center:view', NULL, 1, 1, '13814000-1dd2-11b2-8080-808080808080', '80808080-8080-8080-8080-808080808080', '2024-01-24 11:43:40.971602', NULL, NULL); | |
275 | + | |
276 | +INSERT INTO "public"."sys_dict_item"("id", "dict_id", "item_text", "item_value", "description", "sort", "status", "tenant_id", "creator", "create_time", "updater", "update_time") VALUES ('b963cf7c-bed8-4719-86a0-82d1530c2f1b', '6d53709b-d5b6-4b5e-b9e1-0a55e6c8f9f5', '设备接入信息编辑', 'api:yt:device_profile:access_information:update', NULL, 16, 1, '13814000-1dd2-11b2-8080-808080808080', '80808080-8080-8080-8080-808080808080', '2024-02-22 18:51:34.10582', NULL, NULL); | |
277 | + | |
278 | +INSERT INTO "public"."sys_dict_item"("id", "dict_id", "item_text", "item_value", "description", "sort", "status", "tenant_id", "creator", "create_time", "updater", "update_time") VALUES ('9129d75f-d9e7-4351-bf1c-1664c292c8d9', '6d53709b-d5b6-4b5e-b9e1-0a55e6c8f9f5', '设备接入信息删除', 'api:yt:device_profile:access_information:delete', NULL, 15, 1, '13814000-1dd2-11b2-8080-808080808080', '80808080-8080-8080-8080-808080808080', '2024-02-21 14:52:52.907144', NULL, NULL); | |
279 | + | |
280 | +INSERT INTO "public"."sys_dict_item"("id", "dict_id", "item_text", "item_value", "description", "sort", "status", "tenant_id", "creator", "create_time", "updater", "update_time") VALUES ('b17b1559-8f42-44d5-92d4-122e9f70cb50', 'c5183b83-eb2a-4111-959b-5c7c7e668d94', '设备接入信息', 'deviceManager:deviceAccess:list', NULL, 1, 1, '13814000-1dd2-11b2-8080-808080808080', '80808080-8080-8080-8080-808080808080', '2024-02-19 10:29:24.048745', '80808080-8080-8080-8080-808080808080', '2024-02-19 10:29:39.684479'); | |
281 | + | |
282 | +INSERT INTO "public"."sys_dict_item"("id", "dict_id", "item_text", "item_value", "description", "sort", "status", "tenant_id", "creator", "create_time", "updater", "update_time") VALUES ('c8f7c6d5-4cd2-4c82-87cf-c6d2facecf77', '6d53709b-d5b6-4b5e-b9e1-0a55e6c8f9f5', '设备接入信息新增', 'api:yt:device_profile:access_information:post', NULL, 14, 1, '13814000-1dd2-11b2-8080-808080808080', '80808080-8080-8080-8080-808080808080', '2024-02-21 14:52:12.462541', '80808080-8080-8080-8080-808080808080', '2024-02-21 14:53:15.150577'); | |
283 | + | |
284 | +INSERT INTO "public"."sys_dict_item"("id", "dict_id", "item_text", "item_value", "description", "sort", "status", "tenant_id", "creator", "create_time", "updater", "update_time") VALUES ('f8c06892-f637-4c31-aac4-f935728cf625', 'c5183b83-eb2a-4111-959b-5c7c7e668d94', '个人中心', 'system:personal_center:view', NULL, 1, 1, '13814000-1dd2-11b2-8080-808080808080', '80808080-8080-8080-8080-808080808080', '2024-01-24 11:41:18.521978', NULL, NULL); | |
285 | + | |
286 | +INSERT INTO "public"."sys_menu"("id", "parent_id", "path", "type", "permission", "creator", "create_time", "updater", "update_time", "name", "tenant_id", "component", "redirect", "alias", "case_sensitive", "meta", "sort") VALUES ('00fccddb-cbdc-4abd-9a1f-1c4f049e6ba2', '38b9a21a-aaf5-42fc-a85f-4381bca3303c', NULL, 'SYSADMIN', 'api:yt:device_profile:access_information:delete', '80808080-8080-8080-8080-808080808080', '2024-02-19 16:44:56.138043', '80808080-8080-8080-8080-808080808080', '2024-02-21 11:28:44.753107', '删除', '13814000-1dd2-11b2-8080-808080808080', NULL, NULL, NULL, NULL, '{"title":"删除","menuType":"2","status":"0"}', 4); | |
287 | + | |
288 | +INSERT INTO "public"."sys_menu"("id", "parent_id", "path", "type", "permission", "creator", "create_time", "updater", "update_time", "name", "tenant_id", "component", "redirect", "alias", "case_sensitive", "meta", "sort") VALUES ('38b9a21a-aaf5-42fc-a85f-4381bca3303c', '885ef223-94b1-4b39-89a8-94584183c0be', '/device/deviceaccess', 'SYSADMIN', 'deviceManager:deviceAccess:list', '80808080-8080-8080-8080-808080808080', '2024-02-19 10:24:45.968051', '80808080-8080-8080-8080-808080808080', '2024-02-20 15:42:57.492133', '设备接入信息', '13814000-1dd2-11b2-8080-808080808080', '/device/deviceaccess/index', NULL, NULL, NULL, '{"icon":"ant-design:branches-outlined","title":"设备接入信息","isLink":false,"menuType":"1","ignoreKeepAlive":false,"hideMenu":false,"status":"0"}', 5); | |
289 | + | |
290 | +INSERT INTO "public"."sys_menu"("id", "parent_id", "path", "type", "permission", "creator", "create_time", "updater", "update_time", "name", "tenant_id", "component", "redirect", "alias", "case_sensitive", "meta", "sort") VALUES ('44f65518-2d9e-4fd5-aa3f-8baf816d912f', '38b9a21a-aaf5-42fc-a85f-4381bca3303c', NULL, 'SYSADMIN', 'api:yt:device_profile:access_information:get', '80808080-8080-8080-8080-808080808080', '2024-02-19 16:43:38.877863', '80808080-8080-8080-8080-808080808080', '2024-02-21 11:28:31.362367', '详情', '13814000-1dd2-11b2-8080-808080808080', NULL, NULL, NULL, NULL, '{"title":"详情","menuType":"2","status":"0"}', 3); | |
291 | + | |
292 | +INSERT INTO "public"."sys_menu"("id", "parent_id", "path", "type", "permission", "creator", "create_time", "updater", "update_time", "name", "tenant_id", "component", "redirect", "alias", "case_sensitive", "meta", "sort") VALUES ('68da3a6d-75e8-49f7-a27f-a4ff6bf822db', '815b9cb0-dbdd-4e57-a1c5-2820741d8e5f', NULL, 'SYSADMIN', 'api:yt:video:control:play', '80808080-8080-8080-8080-808080808080', '2024-02-21 17:51:53.607779', '80808080-8080-8080-8080-808080808080', '2024-02-21 17:52:07.960339', '视频点播', '13814000-1dd2-11b2-8080-808080808080', NULL, NULL, NULL, NULL, '{"title":"视频点播","menuType":"2","status":"0"}', 12); | |
293 | + | |
294 | +INSERT INTO "public"."sys_menu"("id", "parent_id", "path", "type", "permission", "creator", "create_time", "updater", "update_time", "name", "tenant_id", "component", "redirect", "alias", "case_sensitive", "meta", "sort") VALUES ('68da3a6d-75e8-49f7-a27f-a4ff6bf832db', '815b9cb0-dbdd-4e57-a1c5-2820741d8e5f', NULL, 'SYSADMIN', 'api:yt:video:control:channel', '80808080-8080-8080-8080-808080808080', '2024-02-21 17:51:53.607779', '80808080-8080-8080-8080-808080808080', '2024-02-21 17:52:07.960339', '视频通道同步', '13814000-1dd2-11b2-8080-808080808080', NULL, NULL, NULL, NULL, '{"title":"视频通道同步","menuType":"2","status":"0"}', 12); | |
295 | + | |
296 | +INSERT INTO "public"."sys_menu"("id", "parent_id", "path", "type", "permission", "creator", "create_time", "updater", "update_time", "name", "tenant_id", "component", "redirect", "alias", "case_sensitive", "meta", "sort") VALUES ('68da3a6d-75e8-49f7-a27f-a4ff6bf833db', '815b9cb0-dbdd-4e57-a1c5-2820741d8e5f', NULL, 'SYSADMIN', 'api:yt:video:control:control', '80808080-8080-8080-8080-808080808080', '2024-02-21 17:51:53.607779', '80808080-8080-8080-8080-808080808080', '2024-02-21 17:52:07.960339', '摄像头控制', '13814000-1dd2-11b2-8080-808080808080', NULL, NULL, NULL, NULL, '{"title":"摄像头控制","menuType":"2","status":"0"}', 12); | |
297 | + | |
298 | +INSERT INTO "public"."sys_menu"("id", "parent_id", "path", "type", "permission", "creator", "create_time", "updater", "update_time", "name", "tenant_id", "component", "redirect", "alias", "case_sensitive", "meta", "sort") VALUES ('966b3dbb-37c7-4cf0-95d1-7d66511f0a3a', '815b9cb0-dbdd-4e57-a1c5-2820741d8e5f', NULL, 'SYSADMIN', 'api:yt:video:control:stop', '80808080-8080-8080-8080-808080808080', '2024-02-21 17:52:44.156915', NULL, NULL, '视频停止点播', '13814000-1dd2-11b2-8080-808080808080', NULL, NULL, NULL, NULL, '{"title":"视频停止点播","menuType":"2","status":"0"}', 13); | |
299 | + | |
300 | +INSERT INTO "public"."sys_menu"("id", "parent_id", "path", "type", "permission", "creator", "create_time", "updater", "update_time", "name", "tenant_id", "component", "redirect", "alias", "case_sensitive", "meta", "sort") VALUES ('b47acbb6-ef7f-4c8a-9ab7-17098b5f3b63', '38b9a21a-aaf5-42fc-a85f-4381bca3303c', NULL, 'SYSADMIN', 'api:yt:device_profile:access_information:post', '80808080-8080-8080-8080-808080808080', '2024-02-19 16:42:38.723093', '80808080-8080-8080-8080-808080808080', '2024-02-21 11:27:57.643463', '新增', '13814000-1dd2-11b2-8080-808080808080', NULL, NULL, NULL, NULL, '{"title":"新增","menuType":"2","status":"0"}', 1); | |
301 | + | |
302 | +INSERT INTO "public"."sys_menu"("id", "parent_id", "path", "type", "permission", "creator", "create_time", "updater", "update_time", "name", "tenant_id", "component", "redirect", "alias", "case_sensitive", "meta", "sort") VALUES ('dac55d7f-4359-4a4f-99f5-4cb728c1efee', '38b9a21a-aaf5-42fc-a85f-4381bca3303c', NULL, 'SYSADMIN', 'api:yt:device_profile:access_information:update', '80808080-8080-8080-8080-808080808080', '2024-02-21 15:34:25.935524', NULL, NULL, '编辑', '13814000-1dd2-11b2-8080-808080808080', NULL, NULL, NULL, NULL, '{"title":"编辑","menuType":"2","status":"0"}', 2); | |
303 | + | |
304 | +UPDATE "public"."tk_data_view_interface" SET "interface_name" = '设备历史轨迹(适合设备历史轨迹地图)', "request_content_type" = 0, "request_origin_url" = 'localhost', "request_http_type" = 'GET', "request_url" = '/api/plugins/telemetry/{entityType}/{entityId}/values/timeseries{?agg,endTs,interval,keys,limit,orderBy,startTs,endTs,useStrictDataTypes}', "request_params" = '{"requestSQLContent":{},"Params":[{"key":"scope","value":"entityType","mores":null,"editDisabled":false,"required":true},{"key":"date_range","value":"startTs,endTs","mores":true,"editDisabled":true,"required":false},{"key":"deviceProfileId,organizationId,entityId,keys","value":"","mores":null,"editDisabled":true,"required":false}],"Header":[{"key":"ContentType","value":"none","required":false}]}', "state" = 1, "creator" = '80808080-8080-8080-8080-808080808080', "create_time" = '2023-12-26 14:36:44.891373', "updater" = '80808080-8080-8080-8080-808080808080', "update_time" = '2024-01-03 14:48:27.96855', "tenant_id" = '13814000-1dd2-11b2-8080-808080808080', "remark" = NULL, "interface_type" = 'SYSTEM', "filter" = 'const longKey = ''longitude'' | |
305 | +const latKey = ''latitude'' | |
306 | +const allKeys = Object.keys(res) | |
307 | + | |
308 | +let list = [] | |
309 | + | |
310 | +//不是结构体 | |
311 | +if (allKeys.includes(longKey)) { | |
312 | + res[longKey].forEach((longItem, longIndex) => { | |
313 | + list.push([ | |
314 | + Number(longItem.value), | |
315 | + Number(res[latKey][longIndex].value), | |
316 | + ]) | |
317 | + }) | |
318 | +} else { | |
319 | + //为结构体 | |
320 | + const values = Object.values(res) | |
321 | + list = values[0].reduce((acc, curr)=> { | |
322 | + const serializeValue = JSON.parse(curr.value) | |
323 | + const { | |
324 | + longitude, latitude | |
325 | + } = serializeValue | |
326 | + acc.push([longitude, latitude]) | |
327 | + return acc | |
328 | + }, []) | |
329 | +} | |
330 | +return list.reverse()' WHERE "id" = 'f65d5637-3e60-48d7-b3cc-6791fb3883fd'; | |
331 | + | |
332 | +INSERT INTO "public"."tk_device_access_information"("id", "intranet_ip", "intranet_port", "outer_net_ip", "outer_net_port", "device_agreement", "sip_extend", "creator", "create_time", "updater", "update_time", "tenant_id") VALUES ('ff1edbc3-0cfd-4263-ae50-cf9f11f9a88d', '127.0.0.1', '5060', '127.0.0.1', '5060', 'GBT28181', '{"serverId":"51010700599000000001","serverDomain":"5101070059","serverPassword":"61332286"}', '80808080-8080-8080-8080-808080808080 ', '2024-02-29 15:29:54', NULL, NULL, '13814000-1dd2-11b2-8080-808080808080'); | |
\ No newline at end of file | ... | ... |
... | ... | @@ -74,7 +74,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt |
74 | 74 | |
75 | 75 | //Thingskit function |
76 | 76 | public static final String CODE_BASED_LOGIN_ENTRY_POINT = "/api/yt/auth/code/login"; |
77 | - public static final String[] YT_NOT_AUTH_API = new String[]{"/api/yt/auth/code/login","/api/yt/third/bind","/api/yt/third/login/*","/api/yt/third/login/id/*", "/api/yt/third/authorize","/api/yt/platform/get","/api/yt/app_design/get", "/api/yt/noauth/**"}; | |
77 | + public static final String[] YT_NOT_AUTH_API = new String[]{"/api/yt/auth/code/login","/api/yt/third/bind","/api/yt/third/login/*","/api/yt/third/login/id/*", "/api/yt/third/authorize","/api/yt/platform/get","/api/yt/app_design/get", "/api/yt/noauth/**","/api/index/hook/**"}; | |
78 | 78 | |
79 | 79 | public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public"; |
80 | 80 | public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token"; | ... | ... |
... | ... | @@ -82,7 +82,7 @@ public class ThingsKitExceptionHandler { |
82 | 82 | SecurityUser currentUser = getCurrentUser(); |
83 | 83 | |
84 | 84 | Asset entity = new Asset(); |
85 | - entity.setName(e.getMessage()); | |
85 | + entity.setName(e.getClass().getName()); | |
86 | 86 | |
87 | 87 | // 请求相关信息 |
88 | 88 | SysLogOperateDTO additionalInfo = new SysLogOperateDTO(); | ... | ... |
1 | 1 | package org.thingsboard.server.controller.yunteng; |
2 | 2 | |
3 | +import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.PAGE; | |
4 | +import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.PAGE_SIZE; | |
5 | + | |
3 | 6 | import io.swagger.annotations.Api; |
4 | 7 | import io.swagger.annotations.ApiOperation; |
5 | 8 | import io.swagger.annotations.ApiParam; |
9 | +import java.util.HashMap; | |
10 | +import java.util.List; | |
11 | +import java.util.concurrent.ExecutionException; | |
6 | 12 | import lombok.RequiredArgsConstructor; |
7 | 13 | import org.springframework.http.HttpStatus; |
8 | 14 | import org.springframework.http.ResponseEntity; |
... | ... | @@ -23,91 +29,87 @@ import org.thingsboard.server.dao.exception.DataValidationException; |
23 | 29 | import org.thingsboard.server.dao.yunteng.service.HomePageService; |
24 | 30 | import org.thingsboard.server.service.security.model.SecurityUser; |
25 | 31 | |
26 | -import java.util.HashMap; | |
27 | -import java.util.List; | |
28 | -import java.util.concurrent.ExecutionException; | |
29 | - | |
30 | -import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.PAGE; | |
31 | -import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.PAGE_SIZE; | |
32 | - | |
33 | 32 | @RequestMapping("api/yt/homepage") |
34 | 33 | @Api(tags = {"首页"}) |
35 | 34 | @RequiredArgsConstructor |
36 | 35 | @RestController |
37 | 36 | public class HomePageController extends BaseController { |
38 | 37 | |
39 | - private final HomePageService homePageService; | |
38 | + private final HomePageService homePageService; | |
40 | 39 | |
41 | - @GetMapping("left/top") | |
42 | - @ApiOperation(value = "获取左侧顶部信息") | |
43 | - public HomePageLeftTopDTO getLeftTopInfo() | |
44 | - throws ThingsboardException, ExecutionException, InterruptedException { | |
45 | - return homePageService.getHomePageLeftTopInfo( | |
46 | - getCurrentUser().isPtSysadmin(), | |
47 | - getCurrentUser().isPtAdmin(), | |
48 | - getCurrentUser().isPtTenantAdmin(), | |
49 | - getCurrentUser().getCurrentTenantId(), | |
50 | - getCurrentUser().getCurrentUserId()); | |
51 | - } | |
40 | + @GetMapping("left/top") | |
41 | + @ApiOperation(value = "获取左侧顶部信息") | |
42 | + public HomePageLeftTopDTO getLeftTopInfo() | |
43 | + throws ThingsboardException, ExecutionException, InterruptedException { | |
44 | + return homePageService.getHomePageLeftTopInfo( | |
45 | + getCurrentUser().isPtSysadmin(), | |
46 | + getCurrentUser().isPtAdmin(), | |
47 | + getCurrentUser().isPtTenantAdmin(), | |
48 | + getCurrentUser().getCurrentTenantId(), | |
49 | + getCurrentUser().getCurrentUserId()); | |
50 | + } | |
52 | 51 | |
53 | - @GetMapping("right/overdue") | |
54 | - @ApiOperation(value = "获取右侧过期租户信息") | |
55 | - @PreAuthorize("@check.checkPermissions({'SYS_ADMIN','PLATFORM_ADMIN'},{})") | |
56 | - public ResponseEntity<TkPageData<TenantDTO>> getRightTopInfo( | |
57 | - @RequestParam(PAGE) int page, @RequestParam(PAGE_SIZE) int pageSize) { | |
58 | - HashMap<String, Object> queryMap = new HashMap<>(); | |
59 | - queryMap.put(PAGE_SIZE, pageSize); | |
60 | - queryMap.put(PAGE, page); | |
61 | - return ResponseEntity.ok(homePageService.getHomePageRightInfo(queryMap)); | |
62 | - } | |
52 | + @GetMapping("right/overdue") | |
53 | + @ApiOperation(value = "获取右侧过期租户信息") | |
54 | + @PreAuthorize("@check.checkPermissions({'SYS_ADMIN','PLATFORM_ADMIN'},{})") | |
55 | + public ResponseEntity<TkPageData<TenantDTO>> getRightTopInfo( | |
56 | + @RequestParam(PAGE) int page, @RequestParam(PAGE_SIZE) int pageSize) { | |
57 | + HashMap<String, Object> queryMap = new HashMap<>(); | |
58 | + queryMap.put(PAGE_SIZE, pageSize); | |
59 | + queryMap.put(PAGE, page); | |
60 | + return ResponseEntity.ok(homePageService.getHomePageRightInfo(queryMap)); | |
61 | + } | |
63 | 62 | |
64 | - @GetMapping("right/top10") | |
65 | - @ApiOperation(value = "获取右侧Top10") | |
66 | - @PreAuthorize("@check.checkPermissions({'SYS_ADMIN','PLATFORM_ADMIN'},{})") | |
67 | - public DeferredResult<List<TenantTransportMessageDTO>> getTop10() { | |
68 | - return homePageService.getTop10(); | |
69 | - } | |
63 | + @GetMapping("right/top10") | |
64 | + @ApiOperation(value = "获取右侧Top10") | |
65 | + @PreAuthorize("@check.checkPermissions({'SYS_ADMIN','PLATFORM_ADMIN'},{})") | |
66 | + public DeferredResult<List<TenantTransportMessageDTO>> getTop10() { | |
67 | + return homePageService.getTop10(); | |
68 | + } | |
70 | 69 | |
71 | - @GetMapping("left/bottom") | |
72 | - @ApiOperation(value = "获取左侧底部信息") | |
73 | - @PreAuthorize("@check.checkPermissions({'SYS_ADMIN','PLATFORM_ADMIN','CUSTOMER_USER'},{})") | |
74 | - public DeferredResult<List<TkTsValue>> getLeftBottomInfo( | |
75 | - @RequestParam(value = "startTs") long startTs, | |
76 | - @RequestParam("endTs") long endTs, | |
77 | - @RequestParam("interval") long interval, | |
78 | - @RequestParam("trend") TrendType trend) | |
79 | - throws ThingsboardException { | |
80 | - String customerId; | |
81 | - if (getCurrentUser().isPtAdmin()) { | |
82 | - customerId = getCurrentUser().getCurrentTenantId(); | |
83 | - } else { | |
84 | - customerId = getCurrentUser().getCustomerId().getId().toString(); | |
85 | - } | |
86 | - boolean isCustomer = getCurrentUser().isCustomerUser(); | |
87 | - if (TrendType.CUSTOMER_MESSAGE_STATISTICAL == trend | |
88 | - || TrendType.CUSTOMER_ALARM_STATISTICAL == trend) { | |
89 | - if (!isCustomer) { | |
90 | - throw new DataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
91 | - } | |
92 | - } | |
93 | - if (TrendType.TENANT_TREND == trend || TrendType.CUSTOMER_TREND == trend) { | |
94 | - if (isCustomer) { | |
95 | - throw new DataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
96 | - } | |
97 | - } | |
98 | - return homePageService.getHomePageLeftBottomInfo( | |
99 | - customerId, startTs, endTs, interval, trend, isCustomer); | |
70 | + @GetMapping("left/bottom") | |
71 | + @ApiOperation(value = "获取左侧底部历史数据") | |
72 | + @PreAuthorize("@check.checkPermissions({'SYS_ADMIN','PLATFORM_ADMIN','CUSTOMER_USER'},{})") | |
73 | + public DeferredResult<List<TkTsValue>> getLeftBottomInfo( | |
74 | + @RequestParam(value = "startTs") long startTs, | |
75 | + @RequestParam("endTs") long endTs, | |
76 | + @RequestParam("interval") long interval, | |
77 | + @RequestParam("trend") TrendType trend) | |
78 | + throws ThingsboardException { | |
79 | + SecurityUser currentUser = getCurrentUser(); | |
80 | + boolean isCustomer = currentUser.isCustomerUser(); | |
81 | + if (TrendType.CUSTOMER_MESSAGE_STATISTICAL == trend | |
82 | + || TrendType.CUSTOMER_ALARM_STATISTICAL == trend) { | |
83 | + if (!isCustomer) { | |
84 | + throw new DataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
85 | + } | |
86 | + } | |
87 | + if (TrendType.TENANT_TREND == trend || TrendType.CUSTOMER_TREND == trend) { | |
88 | + if (isCustomer) { | |
89 | + throw new DataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
90 | + } | |
100 | 91 | } |
92 | + return homePageService.getHomePageLeftBottomInfo( | |
93 | + currentUser.getTenantId(), currentUser.getCustomerId(), startTs, endTs, interval, trend); | |
94 | + } | |
101 | 95 | |
102 | - @GetMapping("app") | |
103 | - @ApiOperation(value = "小程序首页统计信息") | |
104 | - public ResponseEntity<HomePageAppDTO> appStatistics(@ApiParam(value = "只取告警数据") @RequestParam("login") Boolean login) | |
105 | - throws ThingsboardException{ | |
106 | - SecurityUser user = getCurrentUser(); | |
107 | - if(user == null){ | |
108 | - return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); | |
109 | - } | |
110 | - HomePageAppDTO app = homePageService.app(login,user.isPtSysadmin(),user.isPtAdmin(),user.isPtTenantAdmin(),user.getTenantId(),user.getCustomerId()); | |
111 | - return ResponseEntity.ok(app); | |
96 | + @GetMapping("app") | |
97 | + @ApiOperation(value = "小程序首页统计信息") | |
98 | + public ResponseEntity<HomePageAppDTO> appStatistics( | |
99 | + @ApiParam(value = "只取告警数据") @RequestParam("login") Boolean login) | |
100 | + throws ThingsboardException { | |
101 | + SecurityUser user = getCurrentUser(); | |
102 | + if (user == null) { | |
103 | + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); | |
112 | 104 | } |
105 | + HomePageAppDTO app = | |
106 | + homePageService.app( | |
107 | + login, | |
108 | + user.isPtSysadmin(), | |
109 | + user.isPtAdmin(), | |
110 | + user.isPtTenantAdmin(), | |
111 | + user.getTenantId(), | |
112 | + user.getCustomerId()); | |
113 | + return ResponseEntity.ok(app); | |
114 | + } | |
113 | 115 | } | ... | ... |
... | ... | @@ -22,19 +22,18 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; |
22 | 22 | import org.thingsboard.server.common.data.yunteng.common.AddGroup; |
23 | 23 | import org.thingsboard.server.common.data.yunteng.common.DeleteGroup; |
24 | 24 | import org.thingsboard.server.common.data.yunteng.common.UpdateGroup; |
25 | +import org.thingsboard.server.common.data.yunteng.core.exception.ThingsKitException; | |
25 | 26 | import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; |
26 | 27 | import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; |
27 | 28 | import org.thingsboard.server.common.data.yunteng.dto.*; |
28 | 29 | import org.thingsboard.server.common.data.yunteng.dto.thingsmodel.ImportThingsModelDTO; |
29 | -import org.thingsboard.server.common.data.yunteng.enums.FunctionTypeEnum; | |
30 | -import org.thingsboard.server.common.data.yunteng.enums.ImportEnum; | |
31 | -import org.thingsboard.server.common.data.yunteng.enums.OrderTypeEnum; | |
32 | -import org.thingsboard.server.common.data.yunteng.enums.StatusEnum; | |
30 | +import org.thingsboard.server.common.data.yunteng.enums.*; | |
33 | 31 | import org.thingsboard.server.common.data.yunteng.utils.tools.ResponseResult; |
34 | 32 | import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; |
35 | 33 | import org.thingsboard.server.controller.BaseController; |
36 | 34 | import org.thingsboard.server.dao.yunteng.service.ThingsModelService; |
37 | 35 | import org.thingsboard.server.dao.yunteng.service.TkDeviceProfileService; |
36 | +import org.thingsboard.server.utils.ImportModbusUtils; | |
38 | 37 | |
39 | 38 | import java.io.File; |
40 | 39 | import java.io.IOException; |
... | ... | @@ -42,6 +41,7 @@ import java.io.InputStream; |
42 | 41 | import java.util.HashMap; |
43 | 42 | import java.util.List; |
44 | 43 | import java.util.Map; |
44 | +import java.util.regex.Pattern; | |
45 | 45 | |
46 | 46 | import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.*; |
47 | 47 | import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.ORDER_TYPE; |
... | ... | @@ -234,16 +234,221 @@ public class ThingsModelController extends BaseController { |
234 | 234 | return thingsModelService.saveOrUpdate(thingsModelDTO); |
235 | 235 | } |
236 | 236 | |
237 | + @GetMapping("/download/template") | |
238 | + @ApiOperation("excel模板下载") | |
239 | + public ResponseEntity<Resource> downloadTemplate(String type) throws IOException { | |
240 | + String name = "template"+ File.separator; | |
241 | + if(StringUtils.isNotEmpty(type)&&type.equals("modbus")){ | |
242 | + name = name +"modbusTemplates.xls"; | |
243 | + }else{ | |
244 | + name = name+"template.xls"; | |
245 | + } | |
246 | + InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(name); | |
247 | + if(inputStream!=null){ | |
248 | + HttpHeaders headers = new HttpHeaders(); | |
249 | + headers.add(HttpHeaders.CONTENT_DISPOSITION,"attachment;filename=物模型属性导入模板.xls"); | |
250 | + return ResponseEntity.ok() | |
251 | + .headers(headers) | |
252 | + .contentType(MediaType.APPLICATION_OCTET_STREAM) | |
253 | + .body(new InputStreamResource(inputStream)); | |
254 | + }else { | |
255 | + return null; | |
256 | + } | |
257 | + } | |
258 | + | |
259 | + | |
260 | + @PostMapping("/batch/get_tsl") | |
261 | + @ApiOperation("获取多个产品的物模型数据") | |
262 | + public ResponseEntity<List<BatchDeviceProfileInfoDTO>> getDeviceProfilesTSL(@RequestBody DeviceProfileTSLDTO profileTSLDTO) | |
263 | + throws ThingsboardException { | |
264 | + if(null == profileTSLDTO.getDeviceProfileIds() || null == profileTSLDTO.getFunctionTypeEnum() || | |
265 | + profileTSLDTO.getDeviceProfileIds().isEmpty()){ | |
266 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
267 | + } | |
268 | + return ResponseEntity.ok(thingsModelService.getDeviceProfileTSLInfoByIds(getCurrentUser().getCurrentTenantId(), | |
269 | + profileTSLDTO.getDeviceProfileIds(),profileTSLDTO.getFunctionTypeEnum())); | |
270 | + } | |
271 | + | |
237 | 272 | @ApiOperation(value = "通过excel导入物模型") |
238 | 273 | @PreAuthorize("@check.checkPermissions({'SYS_ADMIN','PLATFORM_ADMIN','TENANT_ADMIN'},{'api:yt:things_model:excel_import'})") |
239 | 274 | @PostMapping("/csvImport") |
240 | - public ResponseResult<String> csvImport(String deviceProfileId, String categoryId, | |
275 | + public ResponseResult<String> csvImport(String deviceProfileId, String categoryId,String type, | |
241 | 276 | @RequestPart("file") MultipartFile file) throws Exception { |
242 | 277 | if(file.isEmpty()){ |
243 | - return null; | |
278 | + throw new ThingsKitException(ErrorMessage.INVALID_PARAMETER); | |
244 | 279 | } |
280 | + try{ | |
245 | 281 | Workbook work = WorkbookFactory.create(file.getInputStream()); |
282 | + String tenantId = getCurrentUser().getCurrentTenantId(); | |
283 | + if(type.equals("modbus")){ | |
284 | + return importModbus(deviceProfileId,categoryId,file,tenantId,work); | |
285 | + }else{ | |
286 | + return importModel(deviceProfileId,categoryId,file,tenantId,work); | |
287 | + } | |
288 | + }catch (Exception e){ | |
289 | + throw new TkDataValidationException(ErrorMessage.IMPORT_ERROR.getMessage()); | |
290 | + } | |
291 | + } | |
292 | + | |
293 | + | |
294 | + ResponseResult<String> importModbus(String deviceProfileId, String categoryId,MultipartFile file,String tenantId, Workbook work) throws IOException { | |
295 | + | |
296 | + Sheet sheet = work.getSheetAt(0); | |
297 | + int succeed = 0;//成功数量 | |
298 | + int failed =0;//失败数量 | |
299 | + StringBuffer failedString = new StringBuffer(); | |
300 | + String excel = sheet.getRow(0).getCell(2).toString(); | |
301 | + if(!excel.equals("MODBUS_RTU操作类型*")){ | |
302 | + throw new TkDataValidationException(ErrorMessage.IMPORT_ERROR.getMessage()); | |
303 | + } | |
304 | + for(int i = 1; i<=sheet.getPhysicalNumberOfRows(); i++) { | |
305 | + Row row = sheet.getRow(i); | |
306 | + if(row!=null){ | |
307 | + String functionName = row.getCell(0).toString();//功能名称 | |
308 | + if(StringUtils.isEmpty(functionName)){ | |
309 | + failed++; | |
310 | + failedString.append("第"+i+"条,功能名称未填;"); | |
311 | + continue; | |
312 | + } | |
313 | + String identifier = row.getCell(1).toString();//标识符* | |
314 | + if(StringUtils.isEmpty(identifier)){ | |
315 | + failed++; | |
316 | + failedString.append("第"+i+"条,标识符未填;"); | |
317 | + continue; | |
318 | + } | |
319 | + Boolean identifierState = thingsModelService.getByIdentifier(tenantId,deviceProfileId,categoryId,identifier); | |
320 | + if(!identifierState){ | |
321 | + failed++; | |
322 | + failedString.append("第"+i+"条,标识符已存在;"); | |
323 | + continue; | |
324 | + } | |
325 | + ThingsModelDTO dto = new ThingsModelDTO(); | |
326 | + | |
327 | + String extensionJson =""; | |
328 | + String json =""; | |
329 | + | |
330 | + if(identifier.equals("source")){ | |
331 | + dto.setAccessMode("r"); | |
332 | + extensionJson ="{\"writeOnly\":true}"; | |
333 | + json ="{\"dataType\":{\"type\":\"STRING\",\"specs\":{}}}"; | |
334 | + }else{ | |
335 | + String operationType = row.getCell(2).toString();//MODBUS_RTU操作类型* | |
336 | + if(StringUtils.isEmpty(operationType)){ | |
337 | + failed++; | |
338 | + failedString.append("第"+i+"条,MODBUS_RTU操作类型未填;"); | |
339 | + continue; | |
340 | + } | |
341 | + String originalDataType = row.getCell(3).toString();//MODBUS_RTU数据类型* | |
342 | + if(StringUtils.isEmpty(originalDataType)){ | |
343 | + failed++; | |
344 | + failedString.append("第"+i+"条,MODBUS_RTU数据类型未填;"); | |
345 | + continue; | |
346 | + } | |
347 | + operationType =OperationTypeEnum.getName(operationType); | |
348 | + originalDataType = AttributeSourceDataTypeEnum.getName(originalDataType); | |
349 | + if(!ImportModbusUtils.operationTypeAndOriginalDataTypeStatus(operationType, originalDataType)){ | |
350 | + failed++; | |
351 | + failedString.append("第"+i+"条,操作类型与数据类型不匹配;"); | |
352 | + continue; | |
353 | + } | |
354 | + | |
355 | + | |
356 | + String registerType = row.getCell(5).toString();//寄存器类型 | |
357 | + String registerAddress =row.getCell(6).toString();//寄存器地址 | |
358 | + if(StringUtils.isEmpty(registerType)){ | |
359 | + failed++; | |
360 | + failedString.append("第"+i+"条,寄存器类型未填;"); | |
361 | + continue; | |
362 | + } | |
363 | + //转换校验输入的字符 | |
364 | + if(registerType.equals("HEX")){ | |
365 | + String hexPattern="^[0-9A-Fa-f]+$"; | |
366 | + if(!Pattern.matches(hexPattern,registerAddress)){ | |
367 | + failedString.append("第"+i+"条,寄存器地址格式不正确;"); | |
368 | + continue; | |
369 | + } | |
370 | + } | |
371 | + if(registerType.equals("DEC")){ | |
372 | + try{ | |
373 | + registerAddress = Integer.toHexString(Integer.parseInt(registerAddress)); | |
374 | + } catch (NumberFormatException e) { | |
375 | + failedString.append("第"+i+"条,寄存器地址格式不正确;"); | |
376 | + continue; | |
377 | + } | |
378 | + } | |
379 | + | |
380 | + String bitMask = row.getCell(7)==null?null:row.getCell(7).toString();//比特位* | |
381 | + if(originalDataType.equals("位")&&StringUtils.isEmpty(bitMask)){ | |
382 | + failed++; | |
383 | + failedString.append("第"+i+"条,比特位置(数据类型为位时必填);"); | |
384 | + continue; | |
385 | + } | |
386 | + if(StringUtils.isEmpty(bitMask)){ | |
387 | + bitMask="7"; | |
388 | + } | |
389 | + | |
390 | + String scaling = row.getCell(8)==null?"1":row.getCell(8).toString();//缩放因子 | |
391 | + String registerCount = row.getCell(9)==null?"":row.getCell(9).toString();//寄存器个数 | |
392 | + | |
393 | + if(originalDataType.equals("字符串")&&StringUtils.isEmpty(registerCount)){ | |
394 | + failed++; | |
395 | + failedString.append("第"+i+"条,寄存器个数(数据类型为字符串时必填);"); | |
396 | + continue; | |
397 | + } | |
398 | + | |
399 | + | |
400 | + String unit =""; | |
401 | + String valueRange =""; | |
402 | + String dataType = ImportModbusUtils.getOriginalDataTypeIsDateType(originalDataType); | |
403 | + String cell4 = row.getCell(4)==null?null:row.getCell(4).toString();//单位 | |
404 | + if(cell4!=null){ | |
405 | + String[] dws = cell4.split("/"); | |
406 | + unit = "\"unit\":\""+dws[1]+"\",\"unitName\":\""+cell4+"\""; | |
407 | + } | |
408 | + if(unit.equals("")){ | |
409 | + valueRange = ImportModbusUtils.getOriginalDataTypeIsValueRange(originalDataType); | |
410 | + }else{ | |
411 | + valueRange = ","+ImportModbusUtils.getOriginalDataTypeIsValueRange(originalDataType); | |
412 | + } | |
413 | + json ="{\"dataType\":{\"type\":\""+dataType+"\",\"specs\":{"+unit+valueRange+"}}}"; | |
414 | + extensionJson = | |
415 | + "{\"writeOnly\":"+ImportModbusUtils.getOperationTypeIsWriteOnly(operationType)+ | |
416 | + ",\"bitMask\":"+bitMask+",\"operationType\":\""+operationType+"\",\"originalDataType\":\""+originalDataType+ | |
417 | + "\",\"registerAddress\":\""+registerAddress+"\",\"scaling\":"+scaling+"}"; | |
418 | + if(!registerCount.equals("")){ | |
419 | + extensionJson = extensionJson+",\"registerCount\":"+registerCount; | |
420 | + } | |
421 | + dto.setAccessMode(ImportModbusUtils.getOperationTypeIsRw(operationType)); | |
422 | + } | |
423 | + | |
424 | + dto.setDeviceProfileId(deviceProfileId); | |
425 | + dto.setCategoryId(categoryId); | |
426 | + dto.setFunctionType(FunctionTypeEnum.properties); | |
427 | + dto.setFunctionName(functionName); | |
428 | + dto.setIdentifier(identifier); | |
429 | + JsonNode functionJson = JacksonUtil.toJsonNode(json); | |
430 | + JsonNode extensionDesc = JacksonUtil.toJsonNode(extensionJson); | |
431 | + dto.setFunctionJson(functionJson); | |
432 | + dto.setExtensionDesc(extensionDesc); | |
433 | + dto.setRemark(row.getCell(10)==null?null:row.getCell(10).toString()); | |
434 | + dto.setTenantId(tenantId); | |
435 | + if(dto.getCategoryId()!=null){ | |
436 | + thingsModelService.categorySaveOrUpdate(dto); | |
437 | + }else{ | |
438 | + thingsModelService.saveOrUpdate(dto); | |
439 | + } | |
440 | + succeed++; | |
441 | + } | |
442 | + } | |
443 | + return ResponseResult.success("操作成功,导入"+succeed+"条;失败"+failed+"条;失败原因:"+failedString); | |
444 | + } | |
445 | + | |
446 | + ResponseResult<String> importModel(String deviceProfileId, String categoryId,MultipartFile file,String tenantId, Workbook work) throws IOException { | |
246 | 447 | Sheet sheet = work.getSheetAt(0); |
448 | + String excel = sheet.getRow(0).getCell(2).toString(); | |
449 | + if(!excel.equals("数据类型*")){ | |
450 | + return ResponseResult.failed(500,"操作失败,请使用下载模板excel重新导入"); | |
451 | + } | |
247 | 452 | int succeed = 0;//成功数量 |
248 | 453 | int failed =0;//失败数量 |
249 | 454 | StringBuffer failedString = new StringBuffer(); |
... | ... | @@ -253,41 +458,39 @@ public class ThingsModelController extends BaseController { |
253 | 458 | String functionName = row.getCell(0).toString();//功能名称 |
254 | 459 | if(StringUtils.isEmpty(functionName)){ |
255 | 460 | failed++; |
256 | - failedString.append("第"+i+"条,功能名称未填\r\n"); | |
461 | + failedString.append("第"+i+"条,功能名称未填;"); | |
257 | 462 | continue; |
258 | 463 | } |
259 | 464 | String identifier = row.getCell(1).toString();//标识符* |
260 | 465 | if(StringUtils.isEmpty(identifier)){ |
261 | 466 | failed++; |
262 | - failedString.append("第"+i+"条,标识符未填\r\n"); | |
467 | + failedString.append("第"+i+"条,标识符未填;"); | |
263 | 468 | continue; |
264 | 469 | } |
265 | 470 | String dataType = row.getCell(2).toString();//数据类型* |
266 | 471 | if(StringUtils.isEmpty(dataType)){ |
267 | 472 | failed++; |
268 | - failedString.append("第"+i+"条,数据类型未填\r\n"); | |
473 | + failedString.append("第"+i+"条,数据类型未填;"); | |
269 | 474 | continue; |
270 | 475 | } |
271 | 476 | String accessMode = row.getCell(7).toString();//读写类型* |
272 | 477 | if(StringUtils.isEmpty(accessMode)){ |
273 | 478 | failed++; |
274 | - failedString.append("第"+i+"条,读写类型未填\r\n"); | |
479 | + failedString.append("第"+i+"条,读写类型未填;"); | |
275 | 480 | continue; |
276 | 481 | } |
277 | 482 | Cell dataJons = row.getCell(9);//Josn对象 |
278 | 483 | if(dataType.equals("struct(结构体)")){ |
279 | 484 | if(StringUtils.isEmpty(dataJons.toString())){ |
280 | 485 | failed++; |
281 | - failedString.append("第"+i+"条,如果数据类型选为结构体则必填Json对象,请填入\r\n"); | |
486 | + failedString.append("第"+i+"条,如果数据类型选为结构体则必填Json对象,请填入;"); | |
282 | 487 | continue; |
283 | 488 | } |
284 | 489 | } |
285 | - | |
286 | - String tenantId = getCurrentUser().getCurrentTenantId(); | |
287 | 490 | Boolean identifierState = thingsModelService.getByIdentifier(tenantId,deviceProfileId,categoryId,identifier); |
288 | 491 | if(!identifierState){ |
289 | 492 | failed++; |
290 | - failedString.append("第"+i+"条,标识符已存在\r\n"); | |
493 | + failedString.append("第"+i+"条,标识符已存在;"); | |
291 | 494 | continue; |
292 | 495 | } |
293 | 496 | ThingsModelDTO dto = new ThingsModelDTO(); |
... | ... | @@ -349,32 +552,4 @@ public class ThingsModelController extends BaseController { |
349 | 552 | } |
350 | 553 | return ResponseResult.success("操作成功,导入"+succeed+"条;失败"+failed+"条;失败原因:"+failedString.toString()); |
351 | 554 | } |
352 | - | |
353 | - @GetMapping("/downloadTemplate") | |
354 | - @ApiOperation("excel模板下载") | |
355 | - public ResponseEntity<Resource> downloadTemplate() throws IOException { | |
356 | - InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("template"+ File.separator +"template.xls"); | |
357 | - if(inputStream!=null){ | |
358 | - HttpHeaders headers = new HttpHeaders(); | |
359 | - headers.add(HttpHeaders.CONTENT_DISPOSITION,"attachment;filename=物模型属性导入模板.xls"); | |
360 | - return ResponseEntity.ok() | |
361 | - .headers(headers) | |
362 | - .contentType(MediaType.APPLICATION_OCTET_STREAM) | |
363 | - .body(new InputStreamResource(inputStream)); | |
364 | - }else { | |
365 | - return null; | |
366 | - } | |
367 | - } | |
368 | - | |
369 | - @PostMapping("/batch/get_tsl") | |
370 | - @ApiOperation("获取多个产品的物模型数据") | |
371 | - public ResponseEntity<List<BatchDeviceProfileInfoDTO>> getDeviceProfilesTSL(@RequestBody DeviceProfileTSLDTO profileTSLDTO) | |
372 | - throws ThingsboardException { | |
373 | - if(null == profileTSLDTO.getDeviceProfileIds() || null == profileTSLDTO.getFunctionTypeEnum() || | |
374 | - profileTSLDTO.getDeviceProfileIds().isEmpty()){ | |
375 | - throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
376 | - } | |
377 | - return ResponseEntity.ok(thingsModelService.getDeviceProfileTSLInfoByIds(getCurrentUser().getCurrentTenantId(), | |
378 | - profileTSLDTO.getDeviceProfileIds(),profileTSLDTO.getFunctionTypeEnum())); | |
379 | - } | |
380 | 555 | } | ... | ... |
... | ... | @@ -5,7 +5,6 @@ import io.swagger.annotations.ApiOperation; |
5 | 5 | import io.swagger.annotations.ApiParam; |
6 | 6 | import lombok.RequiredArgsConstructor; |
7 | 7 | import org.springframework.http.ResponseEntity; |
8 | -import org.springframework.security.access.prepost.PreAuthorize; | |
9 | 8 | import org.springframework.validation.annotation.Validated; |
10 | 9 | import org.springframework.web.bind.annotation.*; |
11 | 10 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
... | ... | @@ -65,7 +64,6 @@ public class TkDataViewInterfaceController extends BaseController { |
65 | 64 | |
66 | 65 | @PostMapping |
67 | 66 | @ApiOperation("新增") |
68 | - @PreAuthorize("@check.checkPermissions({},{})") | |
69 | 67 | public ResponseEntity<TkDataViewInterfaceDTO> save( |
70 | 68 | @Validated({AddGroup.class}) @RequestBody TkDataViewInterfaceDTO tkDataViewInterfaceDTO) |
71 | 69 | throws ThingsboardException { |
... | ... | @@ -77,7 +75,6 @@ public class TkDataViewInterfaceController extends BaseController { |
77 | 75 | |
78 | 76 | @PutMapping |
79 | 77 | @ApiOperation("修改") |
80 | - @PreAuthorize("@check.checkPermissions({},{})") | |
81 | 78 | public ResponseEntity<TkDataViewInterfaceDTO> update( |
82 | 79 | @Validated({UpdateGroup.class}) @RequestBody TkDataViewInterfaceDTO TkDataViewInterfaceDTO) |
83 | 80 | throws ThingsboardException { |
... | ... | @@ -88,7 +85,6 @@ public class TkDataViewInterfaceController extends BaseController { |
88 | 85 | |
89 | 86 | @DeleteMapping |
90 | 87 | @ApiOperation("删除") |
91 | - @PreAuthorize("@check.checkPermissions({},{})") | |
92 | 88 | public ResponseEntity<Boolean> delete( |
93 | 89 | @Validated({DeleteGroup.class}) @RequestBody DeleteDTO deleteDTO) |
94 | 90 | throws ThingsboardException { |
... | ... | @@ -98,7 +94,6 @@ public class TkDataViewInterfaceController extends BaseController { |
98 | 94 | |
99 | 95 | @GetMapping("/publish/{id}") |
100 | 96 | @ApiOperation("发布") |
101 | - @PreAuthorize("@check.checkPermissions({},{})") | |
102 | 97 | public ResponseEntity<Boolean> publishInterface(@PathVariable("id") String id) |
103 | 98 | throws ThingsboardException { |
104 | 99 | return ResponseEntity.ok( |
... | ... | @@ -107,7 +102,6 @@ public class TkDataViewInterfaceController extends BaseController { |
107 | 102 | |
108 | 103 | @GetMapping("/cancel_publish/{id}") |
109 | 104 | @ApiOperation("取消发布") |
110 | - @PreAuthorize("@check.checkPermissions({},{})") | |
111 | 105 | public ResponseEntity<Boolean> cancelPublishInterface(@PathVariable("id") String id) |
112 | 106 | throws ThingsboardException { |
113 | 107 | return ResponseEntity.ok( |
... | ... | @@ -116,7 +110,6 @@ public class TkDataViewInterfaceController extends BaseController { |
116 | 110 | } |
117 | 111 | |
118 | 112 | @ApiOperation("根据接口ID,获得接口详情") |
119 | - @PreAuthorize("@check.checkPermissions({},{})") | |
120 | 113 | @RequestMapping( |
121 | 114 | value = "/get_interface_details", |
122 | 115 | params = {"ids"}, |
... | ... | @@ -130,7 +123,6 @@ public class TkDataViewInterfaceController extends BaseController { |
130 | 123 | |
131 | 124 | @GetMapping("/filter_by_interface_type/{type}") |
132 | 125 | @ApiOperation("根据接口类型过滤数据") |
133 | - @PreAuthorize("@check.checkPermissions({},{})") | |
134 | 126 | public List<TkDataViewInterfaceDTO> filterByInterfaceType(@PathVariable("type") String type) |
135 | 127 | throws ThingsboardException { |
136 | 128 | return tkDataViewInterfaceService.filterByInterfaceType( |
... | ... | @@ -139,7 +131,6 @@ public class TkDataViewInterfaceController extends BaseController { |
139 | 131 | |
140 | 132 | @GetMapping("/find_all_interface/{state}") |
141 | 133 | @ApiOperation("查询所有接口") |
142 | - @PreAuthorize("@check.checkPermissions({},{})") | |
143 | 134 | public List<TkDataViewInterfaceDTO> findAll(@PathVariable("state") String state) |
144 | 135 | throws ThingsboardException { |
145 | 136 | return tkDataViewInterfaceService.findAll(state, getCurrentUser().getCurrentTenantId()); |
... | ... | @@ -147,7 +138,6 @@ public class TkDataViewInterfaceController extends BaseController { |
147 | 138 | |
148 | 139 | @GetMapping("/find/can_use_interfaces") |
149 | 140 | @ApiOperation("查询当前用户可使用的接口") |
150 | - @PreAuthorize("@check.checkPermissions({},{})") | |
151 | 141 | public List<TkDataViewInterfaceDTO> findCanUseInterfaces() throws ThingsboardException { |
152 | 142 | boolean isSysAdminOrPtAdmin = getCurrentUser().isPtAdmin() || getCurrentUser().isSystemAdmin(); |
153 | 143 | return tkDataViewInterfaceService.findCanUseInterfaces( | ... | ... |
1 | +package org.thingsboard.server.controller.yunteng; | |
2 | + | |
3 | +import io.swagger.annotations.Api; | |
4 | +import lombok.RequiredArgsConstructor; | |
5 | +import org.springframework.security.access.prepost.PreAuthorize; | |
6 | +import org.springframework.validation.annotation.Validated; | |
7 | +import org.springframework.web.bind.annotation.*; | |
8 | +import org.thingsboard.server.common.data.StringUtils; | |
9 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | |
10 | +import org.thingsboard.server.common.data.yunteng.common.AddGroup; | |
11 | +import org.thingsboard.server.common.data.yunteng.common.DeleteGroup; | |
12 | +import org.thingsboard.server.common.data.yunteng.common.UpdateGroup; | |
13 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | |
14 | +import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; | |
15 | +import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; | |
16 | +import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO; | |
17 | +import org.thingsboard.server.common.data.yunteng.dto.TkDeviceAccessInformationDTO; | |
18 | +import org.thingsboard.server.common.data.yunteng.utils.tools.ResponseResult; | |
19 | +import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; | |
20 | +import org.thingsboard.server.controller.BaseController; | |
21 | +import org.thingsboard.server.dao.yunteng.service.TkDeviceAccessInformationService; | |
22 | + | |
23 | +import java.util.HashMap; | |
24 | + | |
25 | +import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.*; | |
26 | + | |
27 | +@RestController | |
28 | +@RequiredArgsConstructor | |
29 | +@RequestMapping("api/yt/device_profile/access_information") | |
30 | +@Api(tags = {"设备接入信息管理"}) | |
31 | +public class TkDeviceAccessInformationController extends BaseController { | |
32 | + | |
33 | + private final TkDeviceAccessInformationService service; | |
34 | + | |
35 | + @GetMapping(params = {PAGE_SIZE, PAGE}) | |
36 | + public TkPageData<TkDeviceAccessInformationDTO> page( | |
37 | + @RequestParam(PAGE_SIZE) int pageSize, | |
38 | + @RequestParam(PAGE) int page, | |
39 | + @RequestParam(value = "intranetIp", required = false) String intranetIp, | |
40 | + @RequestParam(value = "intranetPort", required = false) String intranetPort, | |
41 | + @RequestParam(value = "outerNetIp", required = false) String outerNetIp, | |
42 | + @RequestParam(value = "outerNetPort", required = false) String outerNetPort, | |
43 | + @RequestParam(value = "deviceAgreement", required = false) String deviceAgreement, | |
44 | + @RequestParam(value = ORDER_FILED, required = false) String orderBy) { | |
45 | + | |
46 | + HashMap<String, Object> queryMap = new HashMap<>(); | |
47 | + queryMap.put(PAGE_SIZE, pageSize); | |
48 | + queryMap.put(PAGE, page); | |
49 | + queryMap.put(ORDER_FILED, orderBy); | |
50 | + queryMap.put("intranetIp", intranetIp); | |
51 | + queryMap.put("intranetPort", intranetPort); | |
52 | + queryMap.put("outerNetIp", outerNetIp); | |
53 | + queryMap.put("outerNetPort", outerNetPort); | |
54 | + queryMap.put("deviceAgreement", deviceAgreement); | |
55 | + | |
56 | + return service.page(queryMap); | |
57 | + } | |
58 | + | |
59 | + @PostMapping | |
60 | + @PreAuthorize( | |
61 | + "@check.checkPermissions({'SYS_ADMIN','PLATFORM_ADMIN'},{'api:yt:device_profile:access_information:post'})") | |
62 | + public ResponseResult<TkDeviceAccessInformationDTO> saveAccessInfo( | |
63 | + @Validated({AddGroup.class}) @RequestBody TkDeviceAccessInformationDTO dto) | |
64 | + throws ThingsboardException { | |
65 | + if (!StringUtils.isEmpty(dto.getId())) { | |
66 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
67 | + } | |
68 | + return saveOrUpdate(dto); | |
69 | + } | |
70 | + | |
71 | + @PutMapping | |
72 | + @PreAuthorize( | |
73 | + "@check.checkPermissions({'SYS_ADMIN','PLATFORM_ADMIN'},{'api:yt:device_profile:access_information:update'})") | |
74 | + public ResponseResult<TkDeviceAccessInformationDTO> updateAccessInfo( | |
75 | + @Validated({UpdateGroup.class}) @RequestBody TkDeviceAccessInformationDTO dto) | |
76 | + throws ThingsboardException { | |
77 | + if (StringUtils.isEmpty(dto.getId())) { | |
78 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
79 | + } | |
80 | + return saveOrUpdate(dto); | |
81 | + } | |
82 | + | |
83 | + @DeleteMapping | |
84 | + @PreAuthorize( | |
85 | + "@check.checkPermissions({'SYS_ADMIN','PLATFORM_ADMIN'},{'api:yt:device_profile:access_information:delete'})") | |
86 | + public ResponseResult<String> deleteSysDict( | |
87 | + @Validated({DeleteGroup.class}) @RequestBody DeleteDTO deleteDTO) | |
88 | + throws ThingsboardException { | |
89 | + deleteDTO.setTenantId(getCurrentUser().getCurrentTenantId()); | |
90 | + return service.delete(deleteDTO) | |
91 | + ? ResponseResult.success(FastIotConstants.StateValue.DELETE_SUCCESS) | |
92 | + : ResponseResult.failed(FastIotConstants.StateValue.DELETE_FAILED); | |
93 | + } | |
94 | + | |
95 | + private ResponseResult<TkDeviceAccessInformationDTO> saveOrUpdate( | |
96 | + TkDeviceAccessInformationDTO dto) throws ThingsboardException { | |
97 | + dto.setTenantId(getCurrentUser().getCurrentTenantId()); | |
98 | + TkDeviceAccessInformationDTO newDTO = service.saveOrUpdate(dto); | |
99 | + return ResponseResult.success(newDTO); | |
100 | + } | |
101 | +} | ... | ... |
... | ... | @@ -33,10 +33,7 @@ import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; |
33 | 33 | import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; |
34 | 34 | import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; |
35 | 35 | import org.thingsboard.server.common.data.yunteng.dto.*; |
36 | -import org.thingsboard.server.common.data.yunteng.enums.DataTypeEnum; | |
37 | -import org.thingsboard.server.common.data.yunteng.enums.DeviceState; | |
38 | -import org.thingsboard.server.common.data.yunteng.enums.DeviceTypeEnum; | |
39 | -import org.thingsboard.server.common.data.yunteng.enums.OrderTypeEnum; | |
36 | +import org.thingsboard.server.common.data.yunteng.enums.*; | |
40 | 37 | import org.thingsboard.server.common.data.yunteng.utils.tools.ResponseResult; |
41 | 38 | import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; |
42 | 39 | import org.thingsboard.server.controller.BaseController; |
... | ... | @@ -225,7 +222,7 @@ public class TkDeviceController extends BaseController { |
225 | 222 | |
226 | 223 | @GetMapping("{id}") |
227 | 224 | @ApiOperation("详情") |
228 | - @PreAuthorize("@check.checkPermissions({},{'api:yt:device:get'})") | |
225 | + @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:device:get'})") | |
229 | 226 | public ResponseEntity<DeviceDTO> getDevice(@PathVariable("id") String id) |
230 | 227 | throws ThingsboardException { |
231 | 228 | return ResponseEntity.of(tkdeviceService.getDevice(getCurrentUser().getCurrentTenantId(), id)); |
... | ... | @@ -239,7 +236,7 @@ public class TkDeviceController extends BaseController { |
239 | 236 | return ResponseEntity.of(tkdeviceService.getDevice(getCurrentUser().getCurrentTenantId(), id)); |
240 | 237 | } |
241 | 238 | |
242 | - @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{})") | |
239 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN','CUSTOMER_USER')") | |
243 | 240 | @PostMapping(params = {PAGE_SIZE, PAGE}) |
244 | 241 | @ApiOperation("查询") |
245 | 242 | public TkPageData<DeviceDTO> pageDevice( |
... | ... | @@ -265,7 +262,7 @@ public class TkDeviceController extends BaseController { |
265 | 262 | return tkdeviceService.page(getCurrentUser().getCurrentTenantId(), queryMap); |
266 | 263 | } |
267 | 264 | |
268 | - @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{})") | |
265 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN','CUSTOMER_USER')") | |
269 | 266 | @GetMapping( |
270 | 267 | path = {"/relation"}, |
271 | 268 | params = {PAGE_SIZE, PAGE}) |
... | ... | @@ -383,14 +380,16 @@ public class TkDeviceController extends BaseController { |
383 | 380 | @ApiParam(value = "设备标签") @RequestParam(value = "deviceLabel", required = false) |
384 | 381 | String deviceLabel, |
385 | 382 | @ApiParam(value = "设备配置ID") @RequestParam(value = "deviceProfileId", required = false) |
386 | - String deviceProfileId) | |
383 | + String deviceProfileId, | |
384 | + @ApiParam(value = "传输协议类型") @RequestParam(value = "transportType", required = false) | |
385 | + TransportTypeEnum transportType) | |
387 | 386 | throws ThingsboardException { |
388 | 387 | return tkdeviceService.findDevicesByDeviceTypeAndOrganizationId( |
389 | 388 | deviceType, |
390 | 389 | getCurrentUser().getCurrentTenantId(), |
391 | 390 | organizationId, |
392 | 391 | deviceLabel, |
393 | - deviceProfileId); | |
392 | + deviceProfileId,transportType); | |
394 | 393 | } |
395 | 394 | |
396 | 395 | |
... | ... | @@ -539,6 +538,12 @@ public class TkDeviceController extends BaseController { |
539 | 538 | tbDevice.setName(deviceDTO.getName()); |
540 | 539 | tbDevice.setCreatedTime(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()); |
541 | 540 | tbDevice.setTenantId(tenantId); |
541 | + tbDevice.setFirmwareId( | |
542 | + StringUtils.isEmpty(deviceDTO.getFirmwareId()) ? null : | |
543 | + new OtaPackageId(UUID.fromString(deviceDTO.getFirmwareId()))); | |
544 | + tbDevice.setSoftwareId( | |
545 | + StringUtils.isEmpty(deviceDTO.getSoftwareId()) ? null : | |
546 | + new OtaPackageId(UUID.fromString(deviceDTO.getSoftwareId()))); | |
542 | 547 | return tbDevice; |
543 | 548 | } |
544 | 549 | ... | ... |
... | ... | @@ -14,6 +14,7 @@ import org.thingsboard.server.common.data.audit.ActionType; |
14 | 14 | import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
15 | 15 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
16 | 16 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
17 | +import org.thingsboard.server.common.data.id.OtaPackageId; | |
17 | 18 | import org.thingsboard.server.common.data.id.RuleChainId; |
18 | 19 | import org.thingsboard.server.common.data.id.TenantId; |
19 | 20 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
... | ... | @@ -119,16 +120,16 @@ public class TkDeviceProfileController extends BaseController { |
119 | 120 | @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{})") |
120 | 121 | public ResponseEntity listDeviceProfile( |
121 | 122 | @ApiParam(value = "设备类型") @RequestParam(value = "deviceType", required = false) |
122 | - DeviceTypeEnum deviceType) | |
123 | + DeviceTypeEnum deviceType,DeviceTransportType transportType) | |
123 | 124 | throws ThingsboardException { |
124 | 125 | List<DeviceProfileDTO> results; |
125 | 126 | String tenantId = getCurrentUser().getCurrentTenantId(); |
126 | 127 | if (getCurrentUser().isTenantAdmin()) { |
127 | - results = tkDeviceProfileService.findDeviceProfile(tenantId, null,deviceType); | |
128 | + results = tkDeviceProfileService.findDeviceProfile(tenantId, null,deviceType,transportType); | |
128 | 129 | } else { |
129 | 130 | results = |
130 | 131 | tkDeviceProfileService.findCustomerDeviceProfiles( |
131 | - tenantId, getCurrentUser().getCustomerId(),deviceType); | |
132 | + tenantId, getCurrentUser().getCustomerId(),deviceType,transportType); | |
132 | 133 | } |
133 | 134 | |
134 | 135 | return ResponseEntity.ok(results); |
... | ... | @@ -257,6 +258,12 @@ public class TkDeviceProfileController extends BaseController { |
257 | 258 | |
258 | 259 | tbDeviceProfile.setProfileData( |
259 | 260 | buildDeviceProfileData(transportType, deviceProfileDTO.getProfileData())); |
261 | + tbDeviceProfile.setFirmwareId( | |
262 | + StringUtils.isEmpty(deviceProfileDTO.getFirmwareId()) ? null : | |
263 | + new OtaPackageId(UUID.fromString(deviceProfileDTO.getFirmwareId()))); | |
264 | + tbDeviceProfile.setSoftwareId( | |
265 | + StringUtils.isEmpty(deviceProfileDTO.getSoftwareId()) ? null : | |
266 | + new OtaPackageId(UUID.fromString(deviceProfileDTO.getSoftwareId()))); | |
260 | 267 | return tbDeviceProfile; |
261 | 268 | } |
262 | 269 | } | ... | ... |
... | ... | @@ -114,7 +114,7 @@ public class TkDeviceScriptController extends BaseController { |
114 | 114 | |
115 | 115 | @GetMapping(params = {PAGE_SIZE, PAGE}) |
116 | 116 | @ApiOperation("分页查询") |
117 | - @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{})") | |
117 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN','CUSTOMER_USER')") | |
118 | 118 | public TkPageData<TkDeviceScriptDTO> pageDeviceScript( |
119 | 119 | @RequestParam(PAGE_SIZE) int pageSize, |
120 | 120 | @RequestParam(PAGE) int page, |
... | ... | @@ -209,7 +209,7 @@ public class TkDeviceScriptController extends BaseController { |
209 | 209 | |
210 | 210 | |
211 | 211 | |
212 | - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") | |
212 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN','CUSTOMER_USER')") | |
213 | 213 | @PostMapping("/modbus") |
214 | 214 | @ApiOperation("生成Modbus指令") |
215 | 215 | public ResponseEntity<String> modbus(@RequestBody TkDeviceRpcDTO inputParams) | ... | ... |
application/src/main/java/org/thingsboard/server/controller/yunteng/TkVideoChannelController.java
0 → 100644
1 | +package org.thingsboard.server.controller.yunteng; | |
2 | + | |
3 | +import io.swagger.annotations.Api; | |
4 | +import io.swagger.annotations.ApiOperation; | |
5 | +import io.swagger.annotations.ApiParam; | |
6 | +import lombok.RequiredArgsConstructor; | |
7 | +import org.springframework.security.access.prepost.PreAuthorize; | |
8 | +import org.springframework.web.bind.annotation.GetMapping; | |
9 | +import org.springframework.web.bind.annotation.RequestMapping; | |
10 | +import org.springframework.web.bind.annotation.RequestParam; | |
11 | +import org.springframework.web.bind.annotation.RestController; | |
12 | +import org.thingsboard.server.common.data.StringUtils; | |
13 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | |
14 | +import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; | |
15 | +import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; | |
16 | +import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO; | |
17 | +import org.thingsboard.server.common.data.yunteng.dto.sip.VideoChanelDTO; | |
18 | +import org.thingsboard.server.common.data.yunteng.enums.OrderTypeEnum; | |
19 | +import org.thingsboard.server.common.data.yunteng.enums.StatusEnum; | |
20 | +import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; | |
21 | +import org.thingsboard.server.controller.BaseController; | |
22 | +import org.thingsboard.server.dao.yunteng.mapper.DeviceMapper; | |
23 | +import org.thingsboard.server.dao.yunteng.service.TkDeviceService; | |
24 | +import org.thingsboard.server.dao.yunteng.service.media.TkVideoChannelService; | |
25 | + | |
26 | +import java.util.HashMap; | |
27 | +import java.util.List; | |
28 | + | |
29 | +import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.*; | |
30 | +import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.ORDER_TYPE; | |
31 | + | |
32 | +@RestController | |
33 | +@RequestMapping("api/yt/video/channel") | |
34 | +@Api(tags = {"视频通道"}) | |
35 | +@RequiredArgsConstructor | |
36 | +public class TkVideoChannelController extends BaseController { | |
37 | + | |
38 | + private final TkVideoChannelService tkVideoChannelService; | |
39 | + private final TkDeviceService tkDeviceService; | |
40 | + private final DeviceMapper deviceMapper; | |
41 | + | |
42 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
43 | + @GetMapping(params = {PAGE_SIZE, PAGE}) | |
44 | + public TkPageData<VideoChanelDTO> pageData( | |
45 | + @RequestParam(PAGE_SIZE) int pageSize, | |
46 | + @RequestParam(PAGE) int page, | |
47 | + @RequestParam(value = ORDER_FILED, required = false) String orderBy, | |
48 | + @RequestParam(value = ORDER_TYPE, required = false) OrderTypeEnum orderType, | |
49 | + @ApiParam(value = "通道所属设备ID(tbDeviceId)") | |
50 | + @RequestParam(value = "tbDeviceId") String tbDeviceId, | |
51 | + @ApiParam(value = "视频通道名称") | |
52 | + @RequestParam(value = "name", required = false) String name, | |
53 | + @ApiParam(value = "设备状态") | |
54 | + @RequestParam(value = "status", required = false) StatusEnum status, | |
55 | + @ApiParam(value = "国标编号") | |
56 | + @RequestParam(value = "cameraCode", required = false) String cameraCode) throws ThingsboardException { | |
57 | + HashMap<String, Object> queryMap = new HashMap<>(); | |
58 | + if(StringUtils.isNotEmpty(name)){ | |
59 | + queryMap.put("name",name); | |
60 | + } | |
61 | + if(StringUtils.isNotEmpty(cameraCode)){ | |
62 | + queryMap.put("cameraCode",cameraCode); | |
63 | + } | |
64 | + if(status!=null){ | |
65 | + queryMap.put("status",status); | |
66 | + } | |
67 | + String tenantId = getCurrentUser().getCurrentTenantId(); | |
68 | + if(StringUtils.isEmpty(tbDeviceId)){ | |
69 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
70 | + } | |
71 | + if(getCurrentUser().isCustomerUser()){ | |
72 | + DeviceDTO deviceDTO = deviceMapper.findDeviceByTbDeviceIdAndCustomerId( | |
73 | + tenantId,tbDeviceId,getCurrentUser().getCustomerId().toString()); | |
74 | + if(null == deviceDTO){ | |
75 | + throw new TkDataValidationException(ErrorMessage.NOT_BELONG_CURRENT_CUSTOMER.getMessage()); | |
76 | + } | |
77 | + } | |
78 | + queryMap.put("tbDeviceId",tbDeviceId); | |
79 | + queryMap.put(PAGE_SIZE, pageSize); | |
80 | + queryMap.put(PAGE, page); | |
81 | + queryMap.put(ORDER_FILED, orderBy); | |
82 | + if (orderType != null) { | |
83 | + queryMap.put(ORDER_TYPE, orderType.name()); | |
84 | + } | |
85 | + return tkVideoChannelService.page(tenantId, queryMap); | |
86 | + } | |
87 | + | |
88 | + @GetMapping("/list") | |
89 | + @ApiOperation("选项列表") | |
90 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
91 | + public List<VideoChanelDTO> listDeviceProfile( | |
92 | + @ApiParam(value = "设备Id") @RequestParam(value = "deviceId", required = false) String deviceId) | |
93 | + throws ThingsboardException { | |
94 | + String tenantId = getCurrentUser().getCurrentTenantId(); | |
95 | + return tkVideoChannelService.getListByDeviceId(deviceId,tenantId); | |
96 | + } | |
97 | +} | ... | ... |
... | ... | @@ -11,6 +11,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; |
11 | 11 | import org.thingsboard.server.common.data.yunteng.common.DeleteGroup; |
12 | 12 | import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO; |
13 | 13 | import org.thingsboard.server.common.data.yunteng.dto.TkVideoDTO; |
14 | +import org.thingsboard.server.common.data.yunteng.dto.TkVideoGbtDTO; | |
14 | 15 | import org.thingsboard.server.common.data.yunteng.enums.OrderTypeEnum; |
15 | 16 | import org.thingsboard.server.common.data.yunteng.utils.tools.ProtocolType; |
16 | 17 | import org.thingsboard.server.common.data.yunteng.utils.tools.ResponseResult; |
... | ... | @@ -30,7 +31,7 @@ import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant. |
30 | 31 | @RequestMapping("api/yt/video") |
31 | 32 | @Api(tags = {"视频流"}) |
32 | 33 | @RequiredArgsConstructor |
33 | -@PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{})") | |
34 | +@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") | |
34 | 35 | public class TkVideoController extends BaseController { |
35 | 36 | |
36 | 37 | private final TkVideoService videoService; |
... | ... | @@ -68,6 +69,17 @@ public class TkVideoController extends BaseController { |
68 | 69 | return videoService.saveOrUpdate(dto); |
69 | 70 | } |
70 | 71 | |
72 | + @PostMapping("/add/gbt28181") | |
73 | + @ApiOperation("新增国标视频") | |
74 | + @PreAuthorize( | |
75 | + "@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:video:post','api:yt:video:update'})") | |
76 | + public TkVideoGbtDTO saveOrUpdateAlarmProfile(@Validated @RequestBody TkVideoGbtDTO dto) | |
77 | + throws ThingsboardException { | |
78 | + dto.setTenantId(getCurrentUser().getCurrentTenantId()); | |
79 | + return videoService.saveGbt(dto); | |
80 | + } | |
81 | + | |
82 | + | |
71 | 83 | @DeleteMapping |
72 | 84 | @ApiOperation("删除") |
73 | 85 | @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:video:delete'})") |
... | ... | @@ -138,8 +150,6 @@ public class TkVideoController extends BaseController { |
138 | 150 | } |
139 | 151 | String url = |
140 | 152 | videoService.getCameraPreviewURL( |
141 | - getCurrentUser().isTenantAdmin(), | |
142 | - getCurrentUser().getCurrentUserId(), | |
143 | 153 | getCurrentUser().getCurrentTenantId(), |
144 | 154 | entityId, |
145 | 155 | protocolType); | ... | ... |
1 | +package org.thingsboard.server.controller.yunteng.media; | |
2 | + | |
3 | +import com.google.common.util.concurrent.MoreExecutors; | |
4 | +import io.swagger.annotations.Api; | |
5 | +import io.swagger.annotations.ApiOperation; | |
6 | +import io.swagger.annotations.ApiParam; | |
7 | +import io.swagger.v3.oas.annotations.Parameter; | |
8 | +import lombok.RequiredArgsConstructor; | |
9 | +import lombok.extern.slf4j.Slf4j; | |
10 | +import org.apache.curator.shaded.com.google.common.util.concurrent.FutureCallback; | |
11 | +import org.apache.curator.shaded.com.google.common.util.concurrent.Futures; | |
12 | +import org.apache.curator.shaded.com.google.common.util.concurrent.ListenableFuture; | |
13 | +import org.checkerframework.checker.nullness.qual.Nullable; | |
14 | +import org.jetbrains.annotations.NotNull; | |
15 | +import org.springframework.security.access.prepost.PreAuthorize; | |
16 | +import org.springframework.web.bind.annotation.*; | |
17 | +import org.springframework.web.context.request.async.DeferredResult; | |
18 | +import org.thingsboard.server.common.data.StringUtils; | |
19 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | |
20 | +import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; | |
21 | +import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; | |
22 | +import org.thingsboard.server.common.data.yunteng.dto.sip.StreamContentDTO; | |
23 | +import org.thingsboard.server.common.data.yunteng.enums.PTZCommandEnum; | |
24 | +import org.thingsboard.server.common.data.yunteng.utils.tools.ResponseResult; | |
25 | +import org.thingsboard.server.controller.BaseController; | |
26 | +import org.thingsboard.server.service.yunteng.media.TkVideoControlService; | |
27 | + | |
28 | +@RestController | |
29 | +@RequestMapping("api/yt/video/control") | |
30 | +@Api(tags = {"视频控制管理"}) | |
31 | +@RequiredArgsConstructor | |
32 | +@Slf4j | |
33 | +public class TkVideoControlController extends BaseController { | |
34 | + | |
35 | + private final TkVideoControlService tkVideoControlService; | |
36 | + | |
37 | + @GetMapping("/start/{deviceId}/{channelId}") | |
38 | + @ApiOperation(value = "视频点播/预览") | |
39 | +// @PreAuthorize("@check.checkPermissions({'SYS_ADMIN','PLATFORM_ADMIN','TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:video:control:play'})") | |
40 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN','PLATFORM_ADMIN','TENANT_ADMIN','CUSTOMER_USER')") | |
41 | + public DeferredResult<ResponseResult<StreamContentDTO>> startPlay( | |
42 | + @PathVariable("deviceId") String tbDeviceId, @PathVariable("channelId") String channelId) | |
43 | + throws ThingsboardException { | |
44 | + if (StringUtils.isEmpty(tbDeviceId) || StringUtils.isEmpty(channelId)) { | |
45 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
46 | + } | |
47 | + DeferredResult<ResponseResult<StreamContentDTO>> response = new DeferredResult<>(); | |
48 | + ListenableFuture<StreamContentDTO> future = | |
49 | + tkVideoControlService.startPlay(getCurrentUser(),tbDeviceId, channelId); | |
50 | + Futures.addCallback( | |
51 | + future, | |
52 | + new FutureCallback<>() { | |
53 | + @Override | |
54 | + public void onSuccess(@Nullable StreamContentDTO streamContentDTO) { | |
55 | + response.setResult(ResponseResult.success(streamContentDTO)); | |
56 | + } | |
57 | + | |
58 | + @Override | |
59 | + public void onFailure(@NotNull Throwable throwable) { | |
60 | + response.setResult(ResponseResult.failed(throwable.getMessage())); | |
61 | + } | |
62 | + }, | |
63 | + MoreExecutors.directExecutor()); | |
64 | + return response; | |
65 | + } | |
66 | + @GetMapping("/sync/{deviceId}") | |
67 | + @ApiOperation(value = "摄像头通道同步") | |
68 | + @PreAuthorize( | |
69 | + "@check.checkPermissions({'SYS_ADMIN','PLATFORM_ADMIN','TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:video:control:channel'})") | |
70 | + public DeferredResult<ResponseResult<String>> freshChannel( | |
71 | + @PathVariable("deviceId") String tbDeviceId) | |
72 | + throws ThingsboardException { | |
73 | + if (StringUtils.isEmpty(tbDeviceId) ) { | |
74 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
75 | + } | |
76 | + DeferredResult<ResponseResult<String>> response = new DeferredResult<>(); | |
77 | + ListenableFuture<String> future = | |
78 | + tkVideoControlService.freshChannel(getCurrentUser(),tbDeviceId); | |
79 | + Futures.addCallback( | |
80 | + future, | |
81 | + new FutureCallback<>() { | |
82 | + @Override | |
83 | + public void onSuccess(@Nullable String result) { | |
84 | + response.setResult(ResponseResult.success(result)); | |
85 | + } | |
86 | + | |
87 | + @Override | |
88 | + public void onFailure(@NotNull Throwable throwable) { | |
89 | + response.setResult(ResponseResult.failed(throwable.getMessage())); | |
90 | + } | |
91 | + }, | |
92 | + MoreExecutors.directExecutor()); | |
93 | + return response; | |
94 | + } | |
95 | + | |
96 | + @ApiOperation(value = "停止点播") | |
97 | + @GetMapping("/stop/{deviceId}/{channelId}") | |
98 | + @PreAuthorize( | |
99 | + "@check.checkPermissions({'SYS_ADMIN','PLATFORM_ADMIN','TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:video:control:stop'})") | |
100 | + public ResponseResult playStop( | |
101 | + @ApiParam(value = "设备ID", required = true) @PathVariable("deviceId") String tbDeviceId, | |
102 | + @ApiParam(value = "通道ID", required = true) @PathVariable("channelId") String channelId) | |
103 | + throws ThingsboardException { | |
104 | + if (StringUtils.isEmpty(tbDeviceId) || StringUtils.isEmpty(channelId)) { | |
105 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
106 | + } | |
107 | + return ResponseResult.success( | |
108 | + tkVideoControlService.stopPlay(getCurrentUser(),tbDeviceId, channelId)); | |
109 | + } | |
110 | + | |
111 | + @ApiOperation(value = "云台控制") | |
112 | + @Parameter(name = "tbDeviceId", description = "TB设备ID", required = true) | |
113 | + @Parameter(name = "channelId", description = "通道国标编号", required = true) | |
114 | + @Parameter( | |
115 | + name = "command", | |
116 | + description = | |
117 | + "控制指令,允许值: LEFT, RIGHT, UP, DOWN, UP_LEFT, UPRIGHT, DOWN_LEFT, DOWN_RIGHT, ZOOM_IN, ZOOM_OUT, STOP", | |
118 | + required = true) | |
119 | + @Parameter(name = "horizonSpeed", description = "水平速度", required = true) | |
120 | + @Parameter(name = "verticalSpeed", description = "垂直速度", required = true) | |
121 | + @Parameter(name = "zoomSpeed", description = "缩放速度", required = true) | |
122 | + @GetMapping("/control/{tbDeviceId}/{channelId}") | |
123 | + @PreAuthorize( | |
124 | + "@check.checkPermissions({'SYS_ADMIN','PLATFORM_ADMIN','TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:video:control:control'})") | |
125 | + public void ptzControl( | |
126 | + @PathVariable String tbDeviceId, | |
127 | + @PathVariable String channelId, | |
128 | + PTZCommandEnum command, | |
129 | + int horizonSpeed, | |
130 | + int verticalSpeed, | |
131 | + int zoomSpeed) | |
132 | + throws ThingsboardException { | |
133 | + ResponseResult.success( | |
134 | + tkVideoControlService.control(getCurrentUser(),tbDeviceId, channelId,command,horizonSpeed,verticalSpeed,zoomSpeed)); | |
135 | + } | |
136 | +} | ... | ... |
1 | +package org.thingsboard.server.controller.yunteng.media; | |
2 | + | |
3 | +import com.fasterxml.jackson.databind.JsonNode; | |
4 | +import io.swagger.annotations.Api; | |
5 | +import io.swagger.annotations.ApiOperation; | |
6 | +import lombok.RequiredArgsConstructor; | |
7 | +import lombok.extern.slf4j.Slf4j; | |
8 | +import org.springframework.web.bind.annotation.*; | |
9 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | |
10 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | |
11 | +import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO; | |
12 | +import org.thingsboard.server.common.data.yunteng.dto.sip.VideoChanelDTO; | |
13 | +import org.thingsboard.server.common.data.yunteng.dto.sip.hook.param.*; | |
14 | +import org.thingsboard.server.controller.BaseController; | |
15 | +import org.thingsboard.server.dao.yunteng.service.TkDeviceService; | |
16 | +import org.thingsboard.server.dao.yunteng.service.media.TkMediaServerService; | |
17 | +import org.thingsboard.server.dao.yunteng.service.media.TkVideoChannelService; | |
18 | +import org.thingsboard.server.service.yunteng.media.TkVideoControlService; | |
19 | + | |
20 | +/** ZLMediaServer的hook事件监听 */ | |
21 | +@RestController | |
22 | +@RequestMapping("api/index/hook") | |
23 | +@Api(tags = {"ZLMediaServer的hook事件监听"}) | |
24 | +@RequiredArgsConstructor | |
25 | +@Slf4j | |
26 | +public class ZLMediaKitHookController extends BaseController { | |
27 | + private final TkMediaServerService tkMediaServerService; | |
28 | + private final TkVideoChannelService tkVideoChannelService; | |
29 | + private final TkVideoControlService controlService; | |
30 | + | |
31 | + @PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8") | |
32 | + @ApiOperation(value = "保持流媒体服务器的心跳") | |
33 | + public HookResult onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param) { | |
34 | + // log.error("ZLMediaKitHook保持流媒体服务器的心跳【on_server_keepalive】,API参数【{}】", | |
35 | + // JacksonUtil.toString(param)); | |
36 | + return tkMediaServerService.zlmOnServerKeepalive(param); | |
37 | + } | |
38 | + | |
39 | + @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8") | |
40 | + @ApiOperation(value = "播放器鉴权事件") | |
41 | + public HookResult onPlay(@RequestBody BaseParam param) { | |
42 | + return tkMediaServerService.zlmOnPlay(param); | |
43 | + } | |
44 | + | |
45 | + @PostMapping(value = "/on_publish", produces = "application/json;charset=UTF-8") | |
46 | + @ApiOperation(value = "rtsp/rtmp/rtp推流鉴权事件") | |
47 | + public HookResultForOnPublish onPublish(@RequestBody BaseParam param) { | |
48 | + return tkMediaServerService.onPublish(param); | |
49 | + } | |
50 | + | |
51 | + @PostMapping(value = "/on_stream_changed", produces = "application/json;charset=UTF-8") | |
52 | + @ApiOperation(value = "rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感") | |
53 | + public HookResult onStreamChanged(@RequestBody OnStreamChangedHookParam param) { | |
54 | + return tkMediaServerService.zlmOnStreamChanged(param); | |
55 | + } | |
56 | + | |
57 | + @ResponseBody | |
58 | + @PostMapping(value = "/on_stream_none_reader", produces = "application/json;charset=UTF-8") | |
59 | + @ApiOperation(value = "流无人观看时事件,用户可以通过此事件选择是否关闭无人看的流") | |
60 | + public JsonNode onStreamNoneReader(@RequestBody OnStreamNoneReaderHookParam param) throws ThingsboardException { | |
61 | + JsonNode result = tkMediaServerService.zlmOnStreamNoneReader(param); | |
62 | + if(result.has(FastIotConstants.ZLMediaBody.CHANNEL_ID) &&result.has(FastIotConstants.ZLMediaBody.CAMERA_CODE)){ | |
63 | + String channelId = result.get(FastIotConstants.ZLMediaBody.CHANNEL_ID).asText(); | |
64 | + String cameraCode = result.get(FastIotConstants.ZLMediaBody.CAMERA_CODE).asText(); | |
65 | + VideoChanelDTO chanelDTO = tkVideoChannelService.findVideoChannelById(cameraCode,channelId, null); | |
66 | + controlService.byeCmdInSsrcTransaction(chanelDTO.getTenantId(), | |
67 | + false, | |
68 | + chanelDTO.getDeviceId(), | |
69 | + cameraCode,channelId,result.get(FastIotConstants.ZLMediaBody.SSRCINFO_STREAM).asText(), | |
70 | + fromDeviceRpcResponse->{ | |
71 | + | |
72 | + }); | |
73 | + tkVideoChannelService.updateVideoChannelStreamId(null, cameraCode, channelId);//storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); | |
74 | + } | |
75 | + return result; | |
76 | + } | |
77 | +} | ... | ... |
... | ... | @@ -15,6 +15,9 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.service.transport; |
17 | 17 | |
18 | +import static org.thingsboard.server.service.transport.BasicCredentialsValidationResult.PASSWORD_MISMATCH; | |
19 | +import static org.thingsboard.server.service.transport.BasicCredentialsValidationResult.VALID; | |
20 | + | |
18 | 21 | import com.fasterxml.jackson.core.JsonProcessingException; |
19 | 22 | import com.fasterxml.jackson.databind.JsonNode; |
20 | 23 | import com.fasterxml.jackson.databind.ObjectMapper; |
... | ... | @@ -23,8 +26,18 @@ import com.google.common.util.concurrent.Futures; |
23 | 26 | import com.google.common.util.concurrent.ListenableFuture; |
24 | 27 | import com.google.common.util.concurrent.MoreExecutors; |
25 | 28 | import com.google.protobuf.ByteString; |
29 | +import java.util.List; | |
30 | +import java.util.Optional; | |
31 | +import java.util.Set; | |
32 | +import java.util.UUID; | |
33 | +import java.util.concurrent.ConcurrentHashMap; | |
34 | +import java.util.concurrent.ConcurrentMap; | |
35 | +import java.util.concurrent.locks.Lock; | |
36 | +import java.util.concurrent.locks.ReentrantLock; | |
37 | +import java.util.stream.Collectors; | |
26 | 38 | import lombok.RequiredArgsConstructor; |
27 | 39 | import lombok.extern.slf4j.Slf4j; |
40 | +import org.apache.zookeeper.Op; | |
28 | 41 | import org.springframework.stereotype.Service; |
29 | 42 | import org.thingsboard.common.util.JacksonUtil; |
30 | 43 | import org.thingsboard.server.cache.ota.OtaPackageDataCache; |
... | ... | @@ -60,9 +73,14 @@ import org.thingsboard.server.common.data.page.PageLink; |
60 | 73 | import org.thingsboard.server.common.data.relation.EntityRelation; |
61 | 74 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
62 | 75 | import org.thingsboard.server.common.data.security.DeviceCredentialsType; |
76 | +import org.thingsboard.server.common.data.yunteng.common.media.VideoStreamSessionManager; | |
77 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | |
63 | 78 | import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO; |
64 | 79 | import org.thingsboard.server.common.data.yunteng.dto.TkDeviceScriptDTO; |
65 | -import org.thingsboard.server.common.data.yunteng.enums.TkScriptFunctionType; | |
80 | +import org.thingsboard.server.common.data.yunteng.dto.sip.SipDeviceDTO; | |
81 | +import org.thingsboard.server.common.data.yunteng.dto.sip.SsrcTransactionDTO; | |
82 | +import org.thingsboard.server.common.data.yunteng.dto.sip.VideoChanelDTO; | |
83 | +import org.thingsboard.server.common.data.yunteng.enums.VideoCmdEnum; | |
66 | 84 | import org.thingsboard.server.common.msg.EncryptionUtil; |
67 | 85 | import org.thingsboard.server.common.msg.TbMsg; |
68 | 86 | import org.thingsboard.server.common.msg.TbMsgDataType; |
... | ... | @@ -77,8 +95,11 @@ import org.thingsboard.server.dao.device.provision.ProvisionResponse; |
77 | 95 | import org.thingsboard.server.dao.ota.OtaPackageService; |
78 | 96 | import org.thingsboard.server.dao.relation.RelationService; |
79 | 97 | import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
98 | +import org.thingsboard.server.dao.yunteng.entities.TkVideoChannelEntity; | |
80 | 99 | import org.thingsboard.server.dao.yunteng.service.TkDeviceScriptService; |
81 | 100 | import org.thingsboard.server.dao.yunteng.service.TkDeviceService; |
101 | +import org.thingsboard.server.dao.yunteng.service.media.TkMediaServerNodeService; | |
102 | +import org.thingsboard.server.dao.yunteng.service.media.TkVideoChannelService; | |
82 | 103 | import org.thingsboard.server.gen.transport.TransportProtos; |
83 | 104 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; |
84 | 105 | import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceCredentialsRequestMsg; |
... | ... | @@ -105,19 +126,6 @@ import org.thingsboard.server.service.install.DefaultSystemDataLoaderService; |
105 | 126 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
106 | 127 | import org.thingsboard.server.service.resource.TbResourceService; |
107 | 128 | |
108 | -import java.util.Arrays; | |
109 | -import java.util.List; | |
110 | -import java.util.Optional; | |
111 | -import java.util.UUID; | |
112 | -import java.util.concurrent.ConcurrentHashMap; | |
113 | -import java.util.concurrent.ConcurrentMap; | |
114 | -import java.util.concurrent.locks.Lock; | |
115 | -import java.util.concurrent.locks.ReentrantLock; | |
116 | -import java.util.stream.Collectors; | |
117 | - | |
118 | -import static org.thingsboard.server.service.transport.BasicCredentialsValidationResult.PASSWORD_MISMATCH; | |
119 | -import static org.thingsboard.server.service.transport.BasicCredentialsValidationResult.VALID; | |
120 | - | |
121 | 129 | /** |
122 | 130 | * Created by ashvayka on 05.10.18. |
123 | 131 | */ |
... | ... | @@ -135,6 +143,10 @@ public class DefaultTransportApiService implements TransportApiService { |
135 | 143 | private final DeviceService deviceService; |
136 | 144 | private final TkDeviceService ytDeviceService; |
137 | 145 | private final TkDeviceScriptService scriptService; |
146 | + private final TkVideoChannelService channelService; | |
147 | + private final TkMediaServerNodeService mediaServerNodeService; | |
148 | + private final VideoStreamSessionManager videoStreamSessionManager; | |
149 | + | |
138 | 150 | private final RelationService relationService; |
139 | 151 | private final DeviceCredentialsService deviceCredentialsService; |
140 | 152 | private final DbCallbackExecutorService dbCallbackExecutorService; |
... | ... | @@ -192,6 +204,9 @@ public class DefaultTransportApiService implements TransportApiService { |
192 | 204 | else if (transportApiRequestMsg.hasScript()) { |
193 | 205 | result = handle(transportApiRequestMsg.getScript()); |
194 | 206 | } |
207 | + else if (transportApiRequestMsg.hasGbt28181RequestMsg()) { | |
208 | + result = handleGbt(transportApiRequestMsg.getGbt28181RequestMsg()); | |
209 | + } | |
195 | 210 | |
196 | 211 | |
197 | 212 | return Futures.transform(Optional.ofNullable(result).orElseGet(this::getEmptyTransportApiResponseFuture), |
... | ... | @@ -685,20 +700,76 @@ public class DefaultTransportApiService implements TransportApiService { |
685 | 700 | private ListenableFuture<TransportApiResponseMsg> handle(TransportProtos.ScriptProto requestMsg) { |
686 | 701 | List<TkDeviceScriptDTO> allScriptes = scriptService.getScriptes(); |
687 | 702 | TransportApiResponseMsg.Builder responseBuilder = TransportApiResponseMsg.newBuilder(); |
688 | - allScriptes.forEach( | |
689 | - item -> { | |
690 | - UUID tenantId = UUID.fromString(item.getTenantId()); | |
691 | - UUID id = UUID.fromString(item.getId()); | |
692 | - responseBuilder.addScriptsResponseMsg( | |
693 | - TransportProtos.ScriptProto.newBuilder() | |
694 | - .setConvertJs(item.getConvertJs()) | |
695 | - .setTenantIdLSB(tenantId.getLeastSignificantBits()) | |
696 | - .setTenantIdMSB(tenantId.getMostSignificantBits()) | |
697 | - .setScriptIdLSB(id.getLeastSignificantBits()) | |
698 | - .setScriptIdMSB(id.getMostSignificantBits()) | |
699 | - .setFunctionType(item.getScriptType().name()) | |
700 | - .setStatus(item.getStatus())); | |
701 | - }); | |
703 | + allScriptes.forEach( | |
704 | + item -> { | |
705 | + UUID tenantId = UUID.fromString(item.getTenantId()); | |
706 | + UUID id = UUID.fromString(item.getId()); | |
707 | + responseBuilder.addScriptsResponseMsg( | |
708 | + TransportProtos.ScriptProto.newBuilder() | |
709 | + .setConvertJs(item.getConvertJs()) | |
710 | + .setTenantIdLSB(tenantId.getLeastSignificantBits()) | |
711 | + .setTenantIdMSB(tenantId.getMostSignificantBits()) | |
712 | + .setScriptIdLSB(id.getLeastSignificantBits()) | |
713 | + .setScriptIdMSB(id.getMostSignificantBits()) | |
714 | + .setFunctionType(item.getScriptType().name()) | |
715 | + .setStatus(item.getStatus())); | |
716 | + }); | |
702 | 717 | return Futures.immediateFuture(responseBuilder.build()); |
703 | 718 | } |
719 | + private ListenableFuture<TransportApiResponseMsg> handleGbt(TransportProtos.Gbt28181RequestMsg requestMsg) { | |
720 | + TransportProtos.Gbt28181ResponseMsg.Builder responseMsgBuilder =TransportProtos.Gbt28181ResponseMsg.newBuilder(); | |
721 | + String tenantId = new UUID(requestMsg.getTenantIdMSB(), requestMsg.getTenantIdLSB()).toString(); | |
722 | + String tbDeviceId = new UUID(requestMsg.getEntityIdMSB(), requestMsg.getEntityIdLSB()).toString(); | |
723 | + String type = requestMsg.getContextType(); | |
724 | + switch (VideoCmdEnum.valueOf(type)){ | |
725 | + case DeviceInfo: | |
726 | + Optional<SipDeviceDTO> camera = dataDecodingEncodingService.decode(requestMsg.getContext().toByteArray()); | |
727 | + camera.ifPresent(sip ->{ | |
728 | + ytDeviceService.updateDeviceInfo(tenantId,tbDeviceId, FastIotConstants.DeviceAdditional.SIP,JacksonUtil.valueToTree(sip)); | |
729 | + }); | |
730 | + | |
731 | + break; | |
732 | + case Catalog: | |
733 | + Optional<List<VideoChanelDTO>> allChannel = dataDecodingEncodingService.decode(requestMsg.getContext().toByteArray()); | |
734 | + allChannel.ifPresent(d->{ | |
735 | + channelService.clearDeviceChannel(d.get(0).getCameraCode()); | |
736 | + List<TkVideoChannelEntity> chanel= d.stream().map(item -> item.getEntity(TkVideoChannelEntity.class)).collect(Collectors.toList()); | |
737 | + channelService.insertBatch(chanel,chanel.size()); | |
738 | + }); | |
739 | + break; | |
740 | + default: | |
741 | + release(tenantId,tbDeviceId); | |
742 | + | |
743 | + } | |
744 | +// byte[] channaelMsgBytes = dataDecodingEncodingService.encode(channelDTO); | |
745 | +// responseMsgBuilder.setContext(ByteString.copyFrom(channaelMsgBytes)); | |
746 | + return Futures.immediateFuture(TransportApiResponseMsg.newBuilder().setGbt28181ResponseMsg(responseMsgBuilder.build()).build()); | |
747 | + } | |
748 | + private void release(String tenantId,String tbDeviceId){ | |
749 | + DeviceDTO device = ytDeviceService.findDeviceInfoByTbDeviceId(tenantId,tbDeviceId); | |
750 | + Optional.ofNullable(device).ifPresent(dev->{ | |
751 | + JsonNode sip = dev.getDeviceInfo().get(FastIotConstants.DeviceAdditional.SIP); | |
752 | + if(!sip.isEmpty() && sip.has(FastIotConstants.ZLMediaBody.CAMERA_CODE)){ | |
753 | + String cameraCode = sip.get(FastIotConstants.ZLMediaBody.CAMERA_CODE).asText(); | |
754 | + Optional<Set<String>> ssrcKeys = | |
755 | + videoStreamSessionManager.getSsrcTransactionCamaraKey(cameraCode); | |
756 | + ssrcKeys.ifPresent(all ->{ | |
757 | + all.forEach(fullKey->{ | |
758 | + videoStreamSessionManager.getSsrcTransaction(fullKey).ifPresent(ssrcTransaction -> { | |
759 | + mediaServerNodeService.releaseSsrc( | |
760 | + ssrcTransaction.getMediaServerId(), ssrcTransaction.getSsrc()); | |
761 | + mediaServerNodeService.closeRTPServer( | |
762 | + ssrcTransaction.getMediaServerId(), ssrcTransaction.getStream()); | |
763 | + videoStreamSessionManager.remove( | |
764 | + cameraCode, ssrcTransaction.getChannelId(), | |
765 | + ssrcTransaction.getStream()); | |
766 | + }); | |
767 | + | |
768 | + }); | |
769 | + }); | |
770 | + } | |
771 | + }); | |
772 | + | |
773 | + | |
774 | + } | |
704 | 775 | } | ... | ... |
application/src/main/java/org/thingsboard/server/service/yunteng/media/TkVideoControlService.java
0 → 100644
1 | +package org.thingsboard.server.service.yunteng.media; | |
2 | + | |
3 | +import com.fasterxml.jackson.databind.JsonNode; | |
4 | +import org.apache.curator.shaded.com.google.common.util.concurrent.ListenableFuture; | |
5 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | |
6 | +import org.thingsboard.server.common.data.yunteng.dto.sip.*; | |
7 | +import org.thingsboard.server.common.data.yunteng.enums.PTZCommandEnum; | |
8 | +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; | |
9 | +import org.thingsboard.server.service.security.model.SecurityUser; | |
10 | + | |
11 | +import java.util.function.Consumer; | |
12 | + | |
13 | +public interface TkVideoControlService { | |
14 | + | |
15 | + /** | |
16 | + * 视频点播 | |
17 | + * | |
18 | + * @param deviceId 设备ID | |
19 | + * @param channelId 通道ID | |
20 | + * @return 视频流播放地址内容 | |
21 | + */ | |
22 | + ListenableFuture<StreamContentDTO> startPlay( | |
23 | + SecurityUser currentUser, String deviceId, String channelId); | |
24 | + | |
25 | + /** | |
26 | + * 摄像头通道同步 | |
27 | + * | |
28 | + * @param deviceId 设备ID | |
29 | + * @return 视频流播放地址内容 | |
30 | + */ | |
31 | + ListenableFuture<String> freshChannel(SecurityUser currentUser, String deviceId); | |
32 | + | |
33 | + /** | |
34 | + * 当点播时的推送处理程序 | |
35 | + * | |
36 | + * @param mediaServerItem 流媒体信息 | |
37 | + * @param response 响应 | |
38 | + * @param deviceId 点播设备ID | |
39 | + * @param channelId 点播设备通道ID | |
40 | + */ | |
41 | + StreamInfoDTO onPublishHandlerForPlay( | |
42 | + MediaServerDTO mediaServerItem, JsonNode response, String deviceId, String channelId); | |
43 | + | |
44 | + /** | |
45 | + * 通过设备ID和通道ID暂停播放 | |
46 | + * | |
47 | + * @param tbDeviceId TB设备的ID | |
48 | + * @param channelId 通道ID | |
49 | + * @return 暂停播放结果 true成功 false失败 | |
50 | + */ | |
51 | + boolean stopPlay(SecurityUser currentUser, String tbDeviceId, String channelId) | |
52 | + throws ThingsboardException; | |
53 | + | |
54 | + /** | |
55 | + * 摄像头控制 | |
56 | + * | |
57 | + * @param currentUser 登录用户 | |
58 | + * @param tbDeviceId 设备ID | |
59 | + * @param channelId 设备通道 | |
60 | + * @param command 控制指令 | |
61 | + * @param horizonSpeed 水平速度 | |
62 | + * @param verticalSpeed 垂直速度 | |
63 | + * @param zoomSpeed 缩放速度 | |
64 | + * @return | |
65 | + */ | |
66 | + boolean control( | |
67 | + SecurityUser currentUser, | |
68 | + String tbDeviceId, | |
69 | + String channelId, | |
70 | + PTZCommandEnum command, | |
71 | + int horizonSpeed, | |
72 | + int verticalSpeed, | |
73 | + int zoomSpeed) | |
74 | + throws ThingsboardException; | |
75 | + | |
76 | + StreamInfoDTO play( | |
77 | + SecurityUser currentUser, | |
78 | + String tbDeviceId, | |
79 | + MediaServerDTO mediaServerItem, | |
80 | + SsrcInfoDTO ssrcInfo, | |
81 | + SipDeviceDTO device, | |
82 | + String channelId); | |
83 | + | |
84 | + public void byeCmdInSsrcTransaction( | |
85 | + String tenantId, | |
86 | + boolean oneWay, | |
87 | + String tbDeviceId, | |
88 | + String cameraCode, | |
89 | + String channelId, | |
90 | + String streamId, | |
91 | + Consumer<FromDeviceRpcResponse> responseConsumer) | |
92 | + throws ThingsboardException; | |
93 | +} | ... | ... |
1 | +package org.thingsboard.server.service.yunteng.media; | |
2 | + | |
3 | +import com.fasterxml.jackson.databind.JsonNode; | |
4 | +import com.fasterxml.jackson.databind.node.ObjectNode; | |
5 | +import java.util.Optional; | |
6 | +import java.util.UUID; | |
7 | +import java.util.Vector; | |
8 | +import java.util.concurrent.CountDownLatch; | |
9 | +import java.util.concurrent.TimeUnit; | |
10 | +import java.util.concurrent.atomic.AtomicReference; | |
11 | +import java.util.function.Consumer; | |
12 | +import javax.sdp.*; | |
13 | +import lombok.RequiredArgsConstructor; | |
14 | +import lombok.extern.slf4j.Slf4j; | |
15 | +import org.apache.curator.shaded.com.google.common.util.concurrent.Futures; | |
16 | +import org.apache.curator.shaded.com.google.common.util.concurrent.ListenableFuture; | |
17 | +import org.springframework.beans.factory.annotation.Value; | |
18 | +import org.springframework.stereotype.Service; | |
19 | +import org.thingsboard.server.common.data.StringUtils; | |
20 | +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; | |
21 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | |
22 | +import org.thingsboard.server.common.data.id.DeviceId; | |
23 | +import org.thingsboard.server.common.data.id.TenantId; | |
24 | +import org.thingsboard.server.common.data.rpc.ToDeviceRpcRequestBody; | |
25 | +import org.thingsboard.server.common.data.yunteng.common.media.VideoStreamSessionManager; | |
26 | +import org.thingsboard.server.common.data.yunteng.common.media.ZlmHttpHookSubscribe; | |
27 | +import org.thingsboard.server.common.data.yunteng.config.media.UserSetting; | |
28 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | |
29 | +import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; | |
30 | +import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; | |
31 | +import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO; | |
32 | +import org.thingsboard.server.common.data.yunteng.dto.sip.*; | |
33 | +import org.thingsboard.server.common.data.yunteng.dto.sip.hook.param.HookSubscribeForStreamChange; | |
34 | +import org.thingsboard.server.common.data.yunteng.enums.*; | |
35 | +import org.thingsboard.server.common.data.yunteng.utils.JacksonUtil; | |
36 | +import org.thingsboard.server.common.data.yunteng.utils.ZLMediaKitRestFulUtils; | |
37 | +import org.thingsboard.server.common.msg.rpc.FromDeviceRpcResponse; | |
38 | +import org.thingsboard.server.common.msg.rpc.ToDeviceRpcRequest; | |
39 | +import org.thingsboard.server.dao.util.yunteng.ZLMediaKitTaskUtils; | |
40 | +import org.thingsboard.server.dao.yunteng.service.TkDeviceService; | |
41 | +import org.thingsboard.server.dao.yunteng.service.media.*; | |
42 | +import org.thingsboard.server.service.rpc.TbCoreDeviceRpcService; | |
43 | +import org.thingsboard.server.service.security.model.SecurityUser; | |
44 | + | |
45 | +@Service | |
46 | +@RequiredArgsConstructor | |
47 | +@Slf4j | |
48 | +public class TkVideoControlServiceImpl implements TkVideoControlService { | |
49 | + | |
50 | + private final TkMediaServerService tkMediaServerService; | |
51 | + private final TkMediaServerNodeService tkMediaServerNodeService; | |
52 | + private final TkDeviceService tkDeviceService; | |
53 | + private final TkVideoChannelService tkVideoChannelService; | |
54 | + private final TkCacheStorageService tkCacheStorageService; | |
55 | + private final ZLMediaKitTaskUtils zlMediaKitTaskUtils; | |
56 | + private final UserSetting userSetting; | |
57 | + private final ZlmHttpHookSubscribe subscribe; | |
58 | + private final VideoStreamSessionManager videoStreamSessionManager; | |
59 | + private final ZLMediaKitRestFulUtils zlMediaKitRestFulUtils; | |
60 | + private final TbCoreDeviceRpcService deviceRpcService; | |
61 | + | |
62 | + @Override | |
63 | + public ListenableFuture<StreamContentDTO> startPlay( | |
64 | + SecurityUser currentUser, String deviceId, String channelId) { | |
65 | + String tenantId = currentUser.getCurrentTenantId(); | |
66 | + // 判断设备、通道是否存在 | |
67 | + DeviceDTO deviceDTO = tkDeviceService.checkDeviceByTenantIdAndId(tenantId, deviceId, true); | |
68 | + SipDeviceDTO sipDeviceDTO = | |
69 | + JacksonUtil.convertValue( | |
70 | + deviceDTO.getDeviceInfo().get(FastIotConstants.DeviceAdditional.SIP), | |
71 | + SipDeviceDTO.class); | |
72 | + // 获取设备的附加信息 | |
73 | + if (null == sipDeviceDTO) { | |
74 | + throw new TkDataValidationException(ErrorMessage.FOUND_VIDEO_DEVICE_FAILED.getMessage()); | |
75 | + } | |
76 | + VideoChanelDTO videoChanelDTO = | |
77 | + tkVideoChannelService.findVideoChannelById( | |
78 | + sipDeviceDTO.getCameraCode(), channelId, tenantId); | |
79 | + if (null == videoChanelDTO) { | |
80 | + throw new TkDataValidationException(ErrorMessage.VIDEO_CHANNEL_NOT_FOUND.getMessage()); | |
81 | + } | |
82 | + // 找到可以使用的流媒体 | |
83 | + MediaServerDTO mediaServer = | |
84 | + tkMediaServerService.getMediaServerForPlay(tenantId, sipDeviceDTO.getMediaServerId()); | |
85 | + if (null == mediaServer) { | |
86 | + throw new TkDataValidationException( | |
87 | + ErrorMessage.NOT_FOUND_MEDIA_SERVER_FOR_PLAY.getMessage()); | |
88 | + } | |
89 | + // 根据设备ID和通道ID,找到对应的流信息 | |
90 | + Optional<StreamInfoDTO> result = | |
91 | + tkCacheStorageService.queryPlayStreamByChannel(sipDeviceDTO.getCameraCode(), channelId); | |
92 | + if (result.isPresent()) { | |
93 | + StreamInfoDTO streamInfoDTO = result.get(); | |
94 | + String streamId = streamInfoDTO.getStream(); | |
95 | + if (StringUtils.isNotEmpty(streamId)) { | |
96 | + String mediaServerId = streamInfoDTO.getMediaServerId(); | |
97 | + Optional<MediaServerDTO> mediaServerDTO = tkMediaServerNodeService.getOne(mediaServerId); | |
98 | + if (mediaServerDTO.isPresent()) { | |
99 | + if (checkRTPServerExist(mediaServerDTO.get(), streamInfoDTO, tenantId)) { | |
100 | + log.debug(String.format("缓存的【流媒体播放信息】内容【%s】", streamInfoDTO)); | |
101 | + return Futures.immediateFuture(new StreamContentDTO(streamInfoDTO)); | |
102 | + } | |
103 | + } | |
104 | + } | |
105 | + } | |
106 | + | |
107 | + StreamInfoDTO streamInfoDTO = | |
108 | + deviceFirstPlay( | |
109 | + currentUser, deviceDTO.getTbDeviceId(), mediaServer, sipDeviceDTO, videoChanelDTO); | |
110 | + if (Optional.ofNullable(streamInfoDTO).isPresent()) { | |
111 | + return Futures.immediateFuture(new StreamContentDTO(streamInfoDTO)); | |
112 | + } else { | |
113 | + return Futures.immediateFuture(new StreamContentDTO(null)); | |
114 | + } | |
115 | + } | |
116 | + | |
117 | + @Override | |
118 | + public ListenableFuture<String> freshChannel(SecurityUser currentUser, String deviceId) { | |
119 | + String tenantId = currentUser.getCurrentTenantId(); | |
120 | + // 判断设备、通道是否存在 | |
121 | + Optional<DeviceDTO> device = Optional.ofNullable(tkDeviceService.checkDeviceByTenantIdAndId(tenantId, deviceId, true)); | |
122 | + if(device.isEmpty()){ | |
123 | + return Futures.immediateFuture(ErrorMessage.DEVICE_NOT_EXTIED.getMessage()); | |
124 | + } | |
125 | + DeviceDTO deviceDTO = device.get(); | |
126 | + if (!deviceDTO.getDeviceState().equals(DeviceState.ONLINE)) { | |
127 | + return Futures.immediateFuture(ErrorMessage.DEVICE_NOT_ONLINE.getMessage()); | |
128 | + } | |
129 | + SipDeviceDTO sipDeviceDTO = | |
130 | + JacksonUtil.convertValue( | |
131 | + deviceDTO.getDeviceInfo().get(FastIotConstants.DeviceAdditional.SIP), | |
132 | + SipDeviceDTO.class); | |
133 | + // 获取设备的附加信息 | |
134 | + if (null == sipDeviceDTO) { | |
135 | + throw new TkDataValidationException(ErrorMessage.FOUND_VIDEO_DEVICE_FAILED.getMessage()); | |
136 | + } | |
137 | + CountDownLatch timeoutLatch = new CountDownLatch(1); | |
138 | + AtomicReference<String> result = new AtomicReference<>(); | |
139 | + // 进行命令发送 | |
140 | + | |
141 | + | |
142 | + ObjectNode paramJson = JacksonUtil.newObjectNode(); | |
143 | + paramJson.put(FastIotConstants.ZLMediaBody.MSG_TYPE, VideoXmlEnum.Query.name()); | |
144 | + int sn = (int) ((Math.random() * 9 + 1) * 100000); | |
145 | + paramJson.put(FastIotConstants.ZLMediaBody.MSG_CONTEXT, sn); | |
146 | + | |
147 | + try { | |
148 | + cameraCommonCmd( | |
149 | + currentUser.getCurrentTenantId(), | |
150 | + paramJson, | |
151 | + sipDeviceDTO.getCameraCode(), | |
152 | + VideoMethodEnum.MESSAGE, | |
153 | + false, | |
154 | + deviceId, | |
155 | + fromDeviceRpcResponse -> { | |
156 | + log.warn( | |
157 | + "【流媒体SIP】收到【视频点播】结果=异常【{}】+数据【{}】", | |
158 | + fromDeviceRpcResponse.getError(), | |
159 | + fromDeviceRpcResponse.getResponse()); | |
160 | + fromDeviceRpcResponse | |
161 | + .getResponse() | |
162 | + .ifPresent( | |
163 | + jsonStr -> { | |
164 | + JsonNode responseJson = JacksonUtil.toJsonNode(jsonStr); | |
165 | + if (fromDeviceRpcResponse.getError().isEmpty()) { | |
166 | + result.set(jsonStr); | |
167 | + } else { | |
168 | + result.set(fromDeviceRpcResponse.getError().get().name()); | |
169 | + } | |
170 | + }); | |
171 | + timeoutLatch.countDown(); | |
172 | + }); | |
173 | + } catch (ThingsboardException e) { | |
174 | + Futures.immediateFailedFuture(e); | |
175 | + } | |
176 | + return Futures.immediateFuture(result.get()); | |
177 | + | |
178 | + } | |
179 | + | |
180 | + @Override | |
181 | + public StreamInfoDTO onPublishHandlerForPlay( | |
182 | + MediaServerDTO mediaServerItem, JsonNode response, String deviceCode, String channelId) { | |
183 | + StreamInfoDTO streamInfo = onPublishHandler(mediaServerItem, response, deviceCode, channelId); | |
184 | + VideoChanelDTO videoChanel = | |
185 | + tkVideoChannelService.findVideoChannelById( | |
186 | + deviceCode, channelId, mediaServerItem.getTenantId()); | |
187 | + if (videoChanel != null) { | |
188 | + videoChanel.setStreamId(streamInfo.getStream()); | |
189 | + tkVideoChannelService.updateVideoChannelStreamId( | |
190 | + streamInfo.getStream(), deviceCode, channelId); | |
191 | + } | |
192 | + tkCacheStorageService.cacheStreamInfoByStartPlay(streamInfo); | |
193 | + return streamInfo; | |
194 | + } | |
195 | + | |
196 | + @Override | |
197 | + public boolean stopPlay(SecurityUser currentUser, String tbDeviceId, String channelId) | |
198 | + throws ThingsboardException { | |
199 | + String tenantId = currentUser.getCurrentTenantId(); | |
200 | + DeviceDTO deviceDTO = tkDeviceService.findDeviceInfoByTbDeviceId(tenantId, tbDeviceId); | |
201 | + if (null == deviceDTO | |
202 | + || !deviceDTO.getDeviceInfo().has(FastIotConstants.DeviceAdditional.SIP)) { | |
203 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
204 | + } | |
205 | + String cameraCode = | |
206 | + deviceDTO | |
207 | + .getDeviceInfo() | |
208 | + .get(FastIotConstants.DeviceAdditional.SIP) | |
209 | + .get(FastIotConstants.ZLMediaBody.CAMERA_CODE) | |
210 | + .asText(); | |
211 | + | |
212 | + Optional<StreamInfoDTO> streamInfoDTO = | |
213 | + tkCacheStorageService.queryPlayStreamByChannel(cameraCode, channelId); | |
214 | + if (streamInfoDTO.isEmpty()) { | |
215 | + throw new TkDataValidationException(ErrorMessage.STREAM_INFO_NOT_FOUND_FOR_PLAY.getMessage()); | |
216 | + } | |
217 | + byeCmdInSsrcTransaction( | |
218 | + currentUser.getCurrentTenantId(), | |
219 | + false, | |
220 | + tbDeviceId, | |
221 | + cameraCode, | |
222 | + channelId, | |
223 | + streamInfoDTO.get().getStream(), | |
224 | + fromDeviceRpcResponse -> {}); | |
225 | + | |
226 | + tkCacheStorageService.deleteCacheStreamInfoByStopPlay( | |
227 | + streamInfoDTO.get()); // redisCatchStorage.stopPlay(streamInfo); | |
228 | + return tkVideoChannelService.updateVideoChannelStreamId( | |
229 | + null, | |
230 | + cameraCode, | |
231 | + channelId); // storager.stopPlay(streamInfo.getDeviceID(), streamInfo.getChannelId()); | |
232 | + } | |
233 | + | |
234 | + @Override | |
235 | + public StreamInfoDTO play( | |
236 | + SecurityUser currentUser, | |
237 | + String tbDeviceId, | |
238 | + MediaServerDTO mediaServerDTO, | |
239 | + SsrcInfoDTO ssrcInfoDTO, | |
240 | + SipDeviceDTO sipDeviceDTO, | |
241 | + String channelId) { | |
242 | + HookSubscribeForStreamChange hookSubscribe = | |
243 | + new HookSubscribeForStreamChange( | |
244 | + "rtp", ssrcInfoDTO.getStream(), true, "rtsp", mediaServerDTO.getMediaServerId()); | |
245 | + CountDownLatch timeoutLatch = new CountDownLatch(1); | |
246 | + // 定时任务超时KEY | |
247 | + String timeOutTaskKey = UUID.randomUUID().toString(); | |
248 | + zlMediaKitTaskUtils.startCronTaskDelay( | |
249 | + timeOutTaskKey, | |
250 | + () -> { | |
251 | + log.debug( | |
252 | + "[点播超时] 收流超时 deviceId: {}, channelId: {},端口:{}, SSRC: {}", | |
253 | + sipDeviceDTO.getCameraCode(), | |
254 | + channelId, | |
255 | + ssrcInfoDTO.getPort(), | |
256 | + ssrcInfoDTO.getSsrc()); | |
257 | + // 点播超时回复BYE 同时释放ssrc以及此次点播的资源 | |
258 | + try { | |
259 | + // cmder.streamByeCmd(device, channelId, ssrcInfo.getStream(), null); | |
260 | + // TODO 回复Bye命令 | |
261 | + } catch (Exception e) { | |
262 | + log.error("[点播超时], 发送BYE失败 {}", e.getMessage()); | |
263 | + } finally { | |
264 | + // timeoutCallback.run(1, "收流超时"); | |
265 | + // 释放SSRC | |
266 | + tkMediaServerNodeService.releaseSsrc( | |
267 | + mediaServerDTO.getMediaServerId(), ssrcInfoDTO.getSsrc()); | |
268 | + // streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream()); | |
269 | + // 关闭RTP服务 | |
270 | + tkMediaServerNodeService.closeRTPServer( | |
271 | + Optional.of(mediaServerDTO), ssrcInfoDTO.getStream()); | |
272 | + // 取消订阅消息监听 | |
273 | + subscribe.removeSubscribe(hookSubscribe); | |
274 | + } | |
275 | + }, | |
276 | + userSetting.getPlayTimeout()); | |
277 | + // 获取端口信息失败,则不发送点播指令 | |
278 | + if (ssrcInfoDTO.getPort() <= FastIotConstants.MagicNumber.ZERO) { | |
279 | + zlMediaKitTaskUtils.stop(timeOutTaskKey); | |
280 | + // 释放SSRC | |
281 | + tkMediaServerNodeService.releaseSsrc( | |
282 | + mediaServerDTO.getMediaServerId(), ssrcInfoDTO.getSsrc()); | |
283 | + videoStreamSessionManager.remove( | |
284 | + sipDeviceDTO.getCameraCode(), channelId, ssrcInfoDTO.getStream()); | |
285 | + throw new TkDataValidationException( | |
286 | + String.format(ErrorMessage.GET_PLAY_PORT_FAILED.getMessage(), mediaServerDTO.getIp())); | |
287 | + } | |
288 | + // 进行命令发送 | |
289 | + AtomicReference<StreamInfoDTO> result = new AtomicReference<>(); | |
290 | + try { | |
291 | + | |
292 | + log.debug( | |
293 | + String.format( | |
294 | + "视频流【%s】的流媒体服务器信息=流媒体服务ID【%s】+流媒体服务端口【%s】+SDP(会话)IP【%s】", | |
295 | + ssrcInfoDTO.getStream(), | |
296 | + mediaServerDTO.getMediaServerId(), | |
297 | + ssrcInfoDTO.getPort(), | |
298 | + mediaServerDTO.getSdpIp())); | |
299 | + ObjectNode paramJson = JacksonUtil.newObjectNode(); | |
300 | + paramJson.set(FastIotConstants.ZLMediaBody.MEDIA, JacksonUtil.valueToTree(mediaServerDTO)); | |
301 | + // | |
302 | + // paramJson.put(FastIotConstants.ZLMediaBody.MEDIA_ID,mediaServerDTO.getMediaServerId()); | |
303 | + // paramJson.put(FastIotConstants.ZLMediaBody.MEDIA_SDP_IP,mediaServerDTO.getSdpIp()); | |
304 | + paramJson.put(FastIotConstants.ZLMediaBody.SSRCINFO_STREAM, ssrcInfoDTO.getStream()); | |
305 | + paramJson.put(FastIotConstants.ZLMediaBody.SSRCINFO_PORT, ssrcInfoDTO.getPort()); | |
306 | + paramJson.put(FastIotConstants.ZLMediaBody.SSRCINFO_SSRC, ssrcInfoDTO.getSsrc()); | |
307 | + paramJson.put(FastIotConstants.ZLMediaBody.CHANNEL_ID, channelId); | |
308 | + // TODO: 2023/12/28 收到流改变事件后,视频点播的业务逻辑。 | |
309 | + subscribe.addSubscribe( | |
310 | + hookSubscribe, | |
311 | + (MediaServerDTO mediaServerItemInUse, JsonNode response) -> { | |
312 | + log.debug( | |
313 | + "处理订阅消息:主题【{}】流媒体信息【{}】收到的内容【{}】", | |
314 | + hookSubscribe.getHookType().name(), | |
315 | + mediaServerItemInUse, | |
316 | + response); | |
317 | + zlMediaKitTaskUtils.stop(timeOutTaskKey); | |
318 | + | |
319 | + subscribe.removeSubscribe(hookSubscribe); | |
320 | + result.set( | |
321 | + steamImage( | |
322 | + ssrcInfoDTO.getStream(), | |
323 | + sipDeviceDTO.getCameraCode(), | |
324 | + channelId, | |
325 | + mediaServerItemInUse, | |
326 | + response)); | |
327 | + timeoutLatch.countDown(); | |
328 | + }); | |
329 | + cameraCommonCmd( | |
330 | + currentUser.getCurrentTenantId(), | |
331 | + paramJson, | |
332 | + sipDeviceDTO.getCameraCode(), | |
333 | + VideoMethodEnum.INVITE, | |
334 | + false, | |
335 | + tbDeviceId, | |
336 | + fromDeviceRpcResponse -> { | |
337 | + log.warn( | |
338 | + "【流媒体SIP】收到【视频点播】结果=异常【{}】+数据【{}】", | |
339 | + fromDeviceRpcResponse.getError(), | |
340 | + fromDeviceRpcResponse.getResponse()); | |
341 | + fromDeviceRpcResponse | |
342 | + .getResponse() | |
343 | + .ifPresent( | |
344 | + jsonStr -> { | |
345 | + JsonNode responseJson = JacksonUtil.toJsonNode(jsonStr); | |
346 | + if (fromDeviceRpcResponse.getError().isEmpty()) { | |
347 | + playSuccess( | |
348 | + currentUser, | |
349 | + tbDeviceId, | |
350 | + mediaServerDTO, | |
351 | + responseJson, | |
352 | + ssrcInfoDTO, | |
353 | + sipDeviceDTO, | |
354 | + channelId, | |
355 | + timeOutTaskKey); | |
356 | + } else { | |
357 | + zlMediaKitTaskUtils.stop(timeOutTaskKey); | |
358 | + tkMediaServerNodeService.closeRTPServer( | |
359 | + mediaServerDTO, ssrcInfoDTO.getStream(), null); | |
360 | + // 释放ssrc | |
361 | + tkMediaServerNodeService.releaseSsrc( | |
362 | + mediaServerDTO.getMediaServerId(), ssrcInfoDTO.getSsrc()); | |
363 | + videoStreamSessionManager.remove( | |
364 | + sipDeviceDTO.getCameraCode(), channelId, ssrcInfoDTO.getStream()); | |
365 | + // errorEvent.response(event); | |
366 | + } | |
367 | + }); | |
368 | + }); | |
369 | + timeoutLatch.await(userSetting.getPlayTimeout(), TimeUnit.MILLISECONDS); | |
370 | + } catch (Exception e) { | |
371 | + zlMediaKitTaskUtils.stop(timeOutTaskKey); | |
372 | + tkMediaServerNodeService.closeRTPServer( | |
373 | + Optional.of(mediaServerDTO), ssrcInfoDTO.getStream()); | |
374 | + // 释放ssrc | |
375 | + tkMediaServerNodeService.releaseSsrc(mediaServerDTO.getId(), ssrcInfoDTO.getSsrc()); | |
376 | + | |
377 | + videoStreamSessionManager.remove( | |
378 | + sipDeviceDTO.getCameraCode(), channelId, ssrcInfoDTO.getStream()); | |
379 | + | |
380 | + // SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(); | |
381 | + // eventResult.type = SipSubscribe.EventResultType.cmdSendFailEvent; | |
382 | + // eventResult.statusCode = -1; | |
383 | + // eventResult.msg = "命令发送失败"; | |
384 | + // errorEvent.response(eventResult); | |
385 | + } | |
386 | + log.debug(String.format("最新的【流媒体播放信息】内容【%s】", result.get())); | |
387 | + return result.get(); | |
388 | + } | |
389 | + | |
390 | + /** | |
391 | + * 调用流媒体接口获取视频流封面图,并触发后续业务 | |
392 | + * | |
393 | + * @param ssrcStream | |
394 | + * @param cameraCode | |
395 | + * @param channelId | |
396 | + * @param mediaServerItemInUse | |
397 | + * @param response | |
398 | + */ | |
399 | + private StreamInfoDTO steamImage( | |
400 | + String ssrcStream, | |
401 | + String cameraCode, | |
402 | + String channelId, | |
403 | + MediaServerDTO mediaServerItemInUse, | |
404 | + JsonNode response) { | |
405 | + // hook响应 | |
406 | + StreamInfoDTO streamInfoDTO = | |
407 | + onPublishHandlerForPlay(mediaServerItemInUse, response, cameraCode, channelId); | |
408 | + String streamUrl; | |
409 | + if (mediaServerItemInUse.getRtspPort() != 0) { | |
410 | + streamUrl = | |
411 | + String.format( | |
412 | + "rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInUse.getRtspPort(), "rtp", ssrcStream); | |
413 | + } else { | |
414 | + streamUrl = | |
415 | + String.format( | |
416 | + "http://127.0.0.1:%s/%s/%s.live.mp4", | |
417 | + mediaServerItemInUse.getHttpPort(), "rtp", ssrcStream); | |
418 | + } | |
419 | + String path = "snap"; | |
420 | + String fileName = cameraCode + "_" + channelId + ".jpg"; | |
421 | + // 请求截图 | |
422 | + log.debug("[请求截图]: " + fileName); | |
423 | + zlMediaKitRestFulUtils.getSnap(mediaServerItemInUse, streamUrl, 15, 1, path, fileName); | |
424 | + return streamInfoDTO; | |
425 | + } | |
426 | + | |
427 | + /** | |
428 | + * 构建视频点播的流媒体信息 | |
429 | + * | |
430 | + * @param mediaServerItem | |
431 | + * @param response | |
432 | + * @param deviceCode | |
433 | + * @param channelId | |
434 | + * @return | |
435 | + */ | |
436 | + private StreamInfoDTO onPublishHandler( | |
437 | + MediaServerDTO mediaServerItem, JsonNode response, String deviceCode, String channelId) { | |
438 | + String streamId = response.get("stream").asText(); | |
439 | + JsonNode tracks = response.get("tracks"); | |
440 | + StreamInfoDTO streamInfo = | |
441 | + tkMediaServerService.getStreamInfoByAppAndStream( | |
442 | + mediaServerItem, "rtp", streamId, tracks, null); | |
443 | + streamInfo.setCameraCode(deviceCode); | |
444 | + streamInfo.setChannelId(channelId); | |
445 | + return streamInfo; | |
446 | + } | |
447 | + | |
448 | + private StreamInfoDTO deviceFirstPlay( | |
449 | + SecurityUser currentUser, | |
450 | + String tbDeviceId, | |
451 | + MediaServerDTO mediaServerDTO, | |
452 | + SipDeviceDTO sipDeviceDTO, | |
453 | + VideoChanelDTO videoChanelDTO) { | |
454 | + // 进行点播时,需要的参数包含:schema、vhost、app、stream | |
455 | + String streamId = null; | |
456 | + // 是否开启了多端口模式 | |
457 | + if (mediaServerDTO.isRtpEnable()) { | |
458 | + streamId = | |
459 | + String.format("%s_%s", sipDeviceDTO.getCameraCode(), videoChanelDTO.getChannelId()); | |
460 | + } | |
461 | + // 开启收流服务 | |
462 | + SsrcInfoDTO ssrcInfoDTO = | |
463 | + tkMediaServerNodeService.openRTPServer( | |
464 | + mediaServerDTO, | |
465 | + streamId, | |
466 | + null, | |
467 | + sipDeviceDTO.isSsrcCheck(), | |
468 | + false, | |
469 | + 0, | |
470 | + false, | |
471 | + sipDeviceDTO.getStreamModeForParam()); | |
472 | + if (null == ssrcInfoDTO) { | |
473 | + throw new TkDataValidationException(ErrorMessage.RECEIVE_STREAM_FAILED.getMessage()); | |
474 | + } | |
475 | + // 开始进行点播 | |
476 | + return play( | |
477 | + currentUser, | |
478 | + tbDeviceId, | |
479 | + mediaServerDTO, | |
480 | + ssrcInfoDTO, | |
481 | + sipDeviceDTO, | |
482 | + videoChanelDTO.getChannelId()); | |
483 | + } | |
484 | + | |
485 | + private boolean checkRTPServerExist( | |
486 | + MediaServerDTO mediaServerDTO, StreamInfoDTO streamInfo, String tenantId) { | |
487 | + String streamId = streamInfo.getStream(); | |
488 | + JsonNode rtpInfo = zlMediaKitRestFulUtils.getRtpInfo(mediaServerDTO, streamId); | |
489 | + if (rtpInfo.get("code").asInt() == 0 && rtpInfo.get("exist").asBoolean()) { | |
490 | + int localPort = rtpInfo.get("local_port").asInt(); | |
491 | + if (localPort == 0) { | |
492 | + throw new TkDataValidationException("点播已经在进行中,请稍候重试"); | |
493 | + } | |
494 | + return true; | |
495 | + } else { | |
496 | + String deviceId = streamInfo.getCameraCode(); | |
497 | + String channelId = streamInfo.getChannelId(); | |
498 | + tkCacheStorageService.deleteCacheStreamInfoByStopPlay(streamInfo); | |
499 | + tkVideoChannelService.updateVideoChannelStreamId(null, deviceId, channelId); | |
500 | + return false; | |
501 | + } | |
502 | + } | |
503 | + | |
504 | + @Value("${server.rest.server_side_rpc.min_timeout:5000}") | |
505 | + protected long minTimeout; | |
506 | + | |
507 | + @Value("${server.rest.server_side_rpc.default_timeout:10000}") | |
508 | + protected long defaultTimeout; | |
509 | + | |
510 | + /** | |
511 | + * 公用的命令下发接口 | |
512 | + * | |
513 | + * @param tenantId | |
514 | + * @param paramJson | |
515 | + * @param oneWay | |
516 | + * @param deviceId TB的设备ID | |
517 | + * @param responseConsumer | |
518 | + * @throws ThingsboardException | |
519 | + */ | |
520 | + public void cameraCommonCmd( | |
521 | + String tenantId, | |
522 | + ObjectNode paramJson, | |
523 | + String cameraCode, | |
524 | + VideoMethodEnum method, | |
525 | + boolean oneWay, | |
526 | + String deviceId, | |
527 | + Consumer<FromDeviceRpcResponse> responseConsumer) | |
528 | + throws ThingsboardException { | |
529 | + try { | |
530 | + | |
531 | + paramJson.put(FastIotConstants.ZLMediaBody.CAMERA_CODE, cameraCode); | |
532 | + paramJson.put(FastIotConstants.ZLMediaBody.METHOD_TYPE, method.name()); | |
533 | + ToDeviceRpcRequestBody body = | |
534 | + new ToDeviceRpcRequestBody("cameraMethod", JacksonUtil.toString(paramJson)); | |
535 | + long timeout = defaultTimeout; | |
536 | + long expTime = System.currentTimeMillis() + Math.max(minTimeout, timeout); | |
537 | + UUID rpcRequestUUID = UUID.randomUUID(); | |
538 | + boolean persisted = false; | |
539 | + ToDeviceRpcRequest rpcRequest = | |
540 | + new ToDeviceRpcRequest( | |
541 | + rpcRequestUUID, | |
542 | + new TenantId(UUID.fromString(tenantId)), | |
543 | + new DeviceId(UUID.fromString(deviceId)), | |
544 | + oneWay, | |
545 | + expTime, | |
546 | + body, | |
547 | + persisted, | |
548 | + null, | |
549 | + null); | |
550 | + deviceRpcService.processRestApiRpcRequest(rpcRequest, responseConsumer, null); | |
551 | + } catch (IllegalArgumentException ioe) { | |
552 | + throw new ThingsboardException( | |
553 | + "Invalid request body", ioe, ThingsboardErrorCode.BAD_REQUEST_PARAMS); | |
554 | + } | |
555 | + } | |
556 | + | |
557 | + /** | |
558 | + * 点播关闭命令下发 | |
559 | + * | |
560 | + * @param oneWay | |
561 | + * @param tbDeviceId TB的设备ID | |
562 | + * @param responseConsumer | |
563 | + * @throws ThingsboardException | |
564 | + */ | |
565 | + @Override | |
566 | + public void byeCmdInSsrcTransaction( | |
567 | + String tenantId, | |
568 | + boolean oneWay, | |
569 | + String tbDeviceId, | |
570 | + String cameraCode, | |
571 | + String channelId, | |
572 | + String streamId, | |
573 | + Consumer<FromDeviceRpcResponse> responseConsumer) | |
574 | + throws ThingsboardException { | |
575 | + Optional<SsrcTransactionDTO> transactionDTO = | |
576 | + videoStreamSessionManager.getSsrcTransaction(cameraCode, channelId, streamId); | |
577 | + if (transactionDTO.isEmpty()) { | |
578 | + throw new TkDataValidationException(ErrorMessage.STREAM_INFO_NOT_FOUND_FOR_PLAY.getMessage()); | |
579 | + } | |
580 | + SsrcTransactionDTO ssrc = transactionDTO.get(); | |
581 | + cameraByeCmd( | |
582 | + tenantId, | |
583 | + ssrc.getSipTransactionInfo(), | |
584 | + oneWay, | |
585 | + true, | |
586 | + tbDeviceId, | |
587 | + cameraCode, | |
588 | + channelId, | |
589 | + streamId, | |
590 | + ssrc.getSsrc(), | |
591 | + ssrc.getMediaServerId(), | |
592 | + responseConsumer); | |
593 | + } | |
594 | + | |
595 | + public void byeCmdInSendRtp( | |
596 | + SecurityUser currentUser, | |
597 | + boolean oneWay, | |
598 | + String tbDeviceId, | |
599 | + String cameraCode, | |
600 | + String channelId, | |
601 | + String streamId, | |
602 | + Consumer<FromDeviceRpcResponse> responseConsumer) | |
603 | + throws ThingsboardException { | |
604 | + | |
605 | + // SendRtpItemDTO sendRtpItem =null; | |
606 | + // SipMessageHeaderDTO sipTransactionInfo = | |
607 | + // SipMessageHeaderDTO.builder() | |
608 | + // .toTag(sendRtpItem.getToTag()) | |
609 | + // .fromTag(sendRtpItem.getFromTag()) | |
610 | + // .callId(sendRtpItem.getCallId()) | |
611 | + // .build(); | |
612 | + // MediaServerDTO mediaServer = | |
613 | + // tkMediaServerService.getMediaServerByMediaServerId(sendRtpItem.getMediaServerId()); | |
614 | + // cameraByeCmd( | |
615 | + // currentUser,ssrc.getSipTransactionInfo(),oneWay,mediaServer != null,tbDeviceId, | |
616 | + // | |
617 | + // cameraCode,sendRtpItem.getChannelId(),streamId,ssrc.getSsrc(),ssrc.getMediaServerId(), | |
618 | + // responseConsumer); | |
619 | + } | |
620 | + | |
621 | + public void cameraByeCmd( | |
622 | + String currentUser, | |
623 | + SipMessageHeaderDTO messageHeaderDTO, | |
624 | + boolean oneWay, | |
625 | + boolean mediaOnline, | |
626 | + String tbDeviceId, | |
627 | + String cameraCode, | |
628 | + String channelId, | |
629 | + String streamId, | |
630 | + String ssrc, | |
631 | + String mediaServerId, | |
632 | + Consumer<FromDeviceRpcResponse> responseConsumer) | |
633 | + throws ThingsboardException { | |
634 | + if (mediaOnline) { | |
635 | + tkMediaServerNodeService.releaseSsrc(mediaServerId, ssrc); | |
636 | + tkMediaServerNodeService.closeRTPServer(mediaServerId, streamId); | |
637 | + } | |
638 | + videoStreamSessionManager.remove(cameraCode, channelId, streamId); | |
639 | + ObjectNode paramJson = JacksonUtil.newObjectNode(); | |
640 | + paramJson.put(FastIotConstants.ZLMediaBody.CHANNEL_ID, channelId); | |
641 | + paramJson.set( | |
642 | + FastIotConstants.ZLMediaBody.MSG_HEADER, JacksonUtil.valueToTree(messageHeaderDTO)); | |
643 | + cameraCommonCmd( | |
644 | + currentUser, | |
645 | + paramJson, | |
646 | + cameraCode, | |
647 | + VideoMethodEnum.BYE, | |
648 | + oneWay, | |
649 | + tbDeviceId, | |
650 | + responseConsumer); | |
651 | + } | |
652 | + | |
653 | + /** | |
654 | + * 点播成功后,流媒体的业务逻辑 | |
655 | + * | |
656 | + * @param currentUser | |
657 | + * @param tbDeviceId | |
658 | + * @param mediaServerDTO | |
659 | + * @param responseJson | |
660 | + * @param ssrcInfo | |
661 | + * @param sipDeviceDTO | |
662 | + * @param channelId | |
663 | + * @param timeOutTaskKey | |
664 | + */ | |
665 | + private void playSuccess( | |
666 | + SecurityUser currentUser, | |
667 | + String tbDeviceId, | |
668 | + MediaServerDTO mediaServerDTO, | |
669 | + JsonNode responseJson, | |
670 | + SsrcInfoDTO ssrcInfo, | |
671 | + SipDeviceDTO sipDeviceDTO, | |
672 | + String channelId, | |
673 | + String timeOutTaskKey) { | |
674 | + String cameraCode = sipDeviceDTO.getCameraCode(); | |
675 | + String callId = responseJson.get(FastIotConstants.ZLMediaBody.CALL_ID).asText(); | |
676 | + JsonNode msgHeader = responseJson.get(FastIotConstants.ZLMediaBody.MSG_HEADER); | |
677 | + SipMessageHeaderDTO sipMessage = JacksonUtil.convertValue(msgHeader, SipMessageHeaderDTO.class); | |
678 | + String sessionType = responseJson.get(FastIotConstants.ZLMediaBody.SESSION_TYPE).asText(); | |
679 | + String streamId = ssrcInfo.getStream(); | |
680 | + videoStreamSessionManager.put( | |
681 | + cameraCode, | |
682 | + channelId, | |
683 | + callId, | |
684 | + streamId, | |
685 | + ssrcInfo.getSsrc(), | |
686 | + sipMessage, | |
687 | + mediaServerDTO.getMediaServerId(), | |
688 | + SessionTypeEnum.valueOf(sessionType)); | |
689 | + // 获取ssrc | |
690 | + String contentString = responseJson.get(FastIotConstants.ZLMediaBody.MSG_CONTEXT).asText(); | |
691 | + int ssrcIndex = contentString.indexOf("y="); | |
692 | + // 检查是否有y字段 | |
693 | + if (ssrcIndex >= 0) { | |
694 | + // ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容 | |
695 | + String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12).trim(); | |
696 | + // 查询到ssrc不一致且开启了ssrc校验则需要针对处理 | |
697 | + if (ssrcInfo.getSsrc().equals(ssrcInResponse)) { | |
698 | + if (sipDeviceDTO.getStreamMode().equalsIgnoreCase("TCP-ACTIVE")) { | |
699 | + String substring = contentString.substring(0, contentString.indexOf("y=")); | |
700 | + try { | |
701 | + SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring); | |
702 | + int port = -1; | |
703 | + Vector mediaDescriptions = sdp.getMediaDescriptions(true); | |
704 | + for (Object description : mediaDescriptions) { | |
705 | + MediaDescription mediaDescription = (MediaDescription) description; | |
706 | + Media media = mediaDescription.getMedia(); | |
707 | + | |
708 | + Vector mediaFormats = media.getMediaFormats(false); | |
709 | + if (mediaFormats.contains("96")) { | |
710 | + port = media.getMediaPort(); | |
711 | + break; | |
712 | + } | |
713 | + } | |
714 | + log.debug( | |
715 | + "[点播-TCP主动连接对方] deviceId: {}, channelId: {}, 连接对方的地址:{}:{}, 收流模式:{}, SSRC: {}, SSRC校验:{}", | |
716 | + sipDeviceDTO.getCameraCode(), | |
717 | + channelId, | |
718 | + sdp.getConnection().getAddress(), | |
719 | + port, | |
720 | + sipDeviceDTO.getStreamMode(), | |
721 | + ssrcInfo.getSsrc(), | |
722 | + sipDeviceDTO.isSsrcCheck()); | |
723 | + JsonNode jsonNode = | |
724 | + zlMediaKitRestFulUtils.connectRtpServer( | |
725 | + mediaServerDTO, sdp.getConnection().getAddress(), port, streamId); | |
726 | + log.debug("[点播-TCP主动连接对方] 结果: {}", jsonNode); | |
727 | + } catch (SdpException e) { | |
728 | + log.error( | |
729 | + "[点播-TCP主动连接对方] deviceId: {}, channelId: {}, 解析200OK的SDP信息失败", | |
730 | + sipDeviceDTO.getCameraCode(), | |
731 | + channelId, | |
732 | + e); | |
733 | + } | |
734 | + } | |
735 | + return; | |
736 | + } | |
737 | + log.debug("[点播消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse); | |
738 | + if (!mediaServerDTO.isRtpEnable() || sipDeviceDTO.isSsrcCheck()) { | |
739 | + log.debug("[点播消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse); | |
740 | + | |
741 | + // 释放不被使用的ssrc | |
742 | + tkMediaServerNodeService.releaseSsrc(mediaServerDTO.getMediaServerId(), ssrcInfo.getSsrc()); | |
743 | + // 单端口模式streamId也有变化,需要重新设置监听 | |
744 | + if (!mediaServerDTO.isRtpEnable()) { | |
745 | + // 添加订阅 | |
746 | + HookSubscribeForStreamChange hookSubscribe = | |
747 | + new HookSubscribeForStreamChange( | |
748 | + "rtp", streamId, true, "rtsp", mediaServerDTO.getMediaServerId()); | |
749 | + subscribe.removeSubscribe(hookSubscribe); | |
750 | + hookSubscribe | |
751 | + .getContent() | |
752 | + .put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase()); | |
753 | + subscribe.addSubscribe( | |
754 | + hookSubscribe, | |
755 | + (MediaServerDTO mediaServerItemInUse, JsonNode response) -> { | |
756 | + log.debug( | |
757 | + "处理订阅消息:主题【{}】流媒体信息【{}】收到的内容【{}】", | |
758 | + hookSubscribe.getHookType().name(), | |
759 | + mediaServerItemInUse, | |
760 | + response); | |
761 | + zlMediaKitTaskUtils.stop(timeOutTaskKey); | |
762 | + // hook响应 | |
763 | + onPublishHandlerForPlay(mediaServerItemInUse, response, cameraCode, channelId); | |
764 | + // hookEvent.response(mediaServerItemInUse, response); | |
765 | + }); | |
766 | + } | |
767 | + | |
768 | + // 关闭rtp server | |
769 | + tkMediaServerNodeService.closeRTPServer( | |
770 | + mediaServerDTO, | |
771 | + streamId, | |
772 | + result -> { | |
773 | + if (result) { | |
774 | + // 重新开启ssrc server | |
775 | + tkMediaServerNodeService.openRTPServer( | |
776 | + mediaServerDTO, | |
777 | + streamId, | |
778 | + ssrcInResponse, | |
779 | + sipDeviceDTO.isSsrcCheck(), | |
780 | + false, | |
781 | + ssrcInfo.getPort(), | |
782 | + true, | |
783 | + sipDeviceDTO.getStreamModeForParam()); | |
784 | + } else { | |
785 | + try { | |
786 | + log.warn("[停止点播] {}/{}", cameraCode, channelId); | |
787 | + byeCmdInSsrcTransaction( | |
788 | + currentUser.getCurrentTenantId(), | |
789 | + false, | |
790 | + tbDeviceId, | |
791 | + cameraCode, | |
792 | + channelId, | |
793 | + streamId, | |
794 | + fromDeviceRpcResponse -> {}); | |
795 | + } catch (Exception e) { | |
796 | + log.error("[命令发送失败] 停止点播, 发送BYE: {}", e.getMessage()); | |
797 | + } | |
798 | + zlMediaKitTaskUtils.stop(timeOutTaskKey); | |
799 | + videoStreamSessionManager.remove(cameraCode, channelId, streamId); | |
800 | + // event.msg = "下级自定义了ssrc,重新设置收流信息失败"; | |
801 | + // event.statusCode = 500; | |
802 | + // errorEvent.response(event); | |
803 | + } | |
804 | + }); | |
805 | + } | |
806 | + } | |
807 | + } | |
808 | + | |
809 | + @Override | |
810 | + public boolean control( | |
811 | + SecurityUser currentUser, | |
812 | + String tbDeviceId, | |
813 | + String channelId, | |
814 | + PTZCommandEnum command, | |
815 | + int horizonSpeed, | |
816 | + int verticalSpeed, | |
817 | + int zoomSpeed) | |
818 | + throws ThingsboardException { | |
819 | + | |
820 | + if (PTZCommandEnum.STOP.equals(command)) { | |
821 | + horizonSpeed = 0; | |
822 | + verticalSpeed = 0; | |
823 | + zoomSpeed = 0; | |
824 | + } | |
825 | + DeviceDTO deviceDTO = | |
826 | + tkDeviceService.checkDeviceByTenantIdAndId( | |
827 | + currentUser.getCurrentTenantId(), tbDeviceId, true); | |
828 | + if (null == deviceDTO) { | |
829 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
830 | + } | |
831 | + SipDeviceDTO sipDeviceDTO = | |
832 | + JacksonUtil.convertValue( | |
833 | + deviceDTO.getDeviceInfo().get(FastIotConstants.DeviceAdditional.SIP), | |
834 | + SipDeviceDTO.class); | |
835 | + if (null == sipDeviceDTO) { | |
836 | + throw new TkDataValidationException(ErrorMessage.INVALID_PARAMETER.getMessage()); | |
837 | + } | |
838 | + PTZCmdDTO ptzCmdDTO = new PTZCmdDTO(); | |
839 | + ptzCmdDTO.setCommand(command); | |
840 | + ptzCmdDTO.setHorizonSpeed(horizonSpeed); | |
841 | + ptzCmdDTO.setVerticalSpeed(verticalSpeed); | |
842 | + ptzCmdDTO.setZoomSpeed(zoomSpeed); | |
843 | + ObjectNode requestJson = JacksonUtil.newObjectNode(); | |
844 | + requestJson.put(FastIotConstants.ZLMediaBody.CHANNEL_ID, channelId); | |
845 | + requestJson.put(FastIotConstants.ZLMediaBody.MSG_TYPE, VideoXmlEnum.Control.name()); | |
846 | + requestJson.set( | |
847 | + FastIotConstants.ZLMediaBody.MSG_CONTEXT, JacksonUtil.valueToTree(ptzCmdDTO)); | |
848 | + cameraCommonCmd( | |
849 | + currentUser.getCurrentTenantId(), | |
850 | + requestJson, | |
851 | + sipDeviceDTO.getCameraCode(), | |
852 | + VideoMethodEnum.MESSAGE, | |
853 | + false, | |
854 | + tbDeviceId, | |
855 | + fromDeviceRpcResponse -> {}); | |
856 | + return false; | |
857 | + } | |
858 | +} | ... | ... |
application/src/main/java/org/thingsboard/server/service/yunteng/media/ZLMediaKitStateRunner.java
0 → 100644
1 | +package org.thingsboard.server.service.yunteng.media; | |
2 | + | |
3 | +import com.fasterxml.jackson.databind.JsonNode; | |
4 | +import lombok.Getter; | |
5 | +import lombok.extern.slf4j.Slf4j; | |
6 | +import org.springframework.beans.factory.annotation.Value; | |
7 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | |
8 | +import org.springframework.stereotype.Component; | |
9 | +import org.thingsboard.server.common.data.id.EntityId; | |
10 | +import org.thingsboard.server.common.data.yunteng.config.media.ZLMediaKitServerConfig; | |
11 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | |
12 | +import org.thingsboard.server.common.data.yunteng.dto.sip.MediaServerDTO; | |
13 | +import org.thingsboard.server.common.data.yunteng.utils.JacksonUtil; | |
14 | +import org.thingsboard.server.common.data.yunteng.utils.ZLMediaKitRestFulUtils; | |
15 | +import org.thingsboard.server.dao.util.yunteng.ZLMediaKitTaskUtils; | |
16 | +import org.thingsboard.server.dao.yunteng.service.media.TkMediaServerNodeService; | |
17 | +import org.thingsboard.server.dao.yunteng.service.media.TkMediaServerService; | |
18 | +import org.thingsboard.server.common.data.yunteng.config.media.MediaConfig; | |
19 | +import org.thingsboard.server.queue.util.TbCoreComponent; | |
20 | + | |
21 | +import javax.annotation.PostConstruct; | |
22 | +import java.util.List; | |
23 | +import java.util.Map; | |
24 | +import java.util.Optional; | |
25 | + | |
26 | +@Component("ZLMediaKitState") | |
27 | +@TbCoreComponent | |
28 | +@ConditionalOnExpression( | |
29 | + "'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.gbt28181.enabled}'=='true')") | |
30 | +@Slf4j | |
31 | +public class ZLMediaKitStateRunner { | |
32 | + private final ZLMediaKitRestFulUtils zlMediaKitRestFulUtils; | |
33 | + private final TkMediaServerNodeService tkMediaServerNodeService; | |
34 | + private final TkMediaServerService tkMediaServerService; | |
35 | + private final MediaConfig mediaConfig; | |
36 | + private final ZLMediaKitTaskUtils zlMediaKitTaskUtils; | |
37 | + private final Map<String, Boolean> startGetMedia; | |
38 | + | |
39 | + @Value("${media.defaultStateCheckIntervalInSec}") | |
40 | + @Getter | |
41 | + private int defaultStateCheckIntervalInSec; | |
42 | + | |
43 | + public ZLMediaKitStateRunner( | |
44 | + ZLMediaKitRestFulUtils zlMediaKitRestFulUtils, | |
45 | + TkMediaServerNodeService tkMediaServerNodeService, | |
46 | + TkMediaServerService tkMediaServerService, | |
47 | + MediaConfig mediaConfig, | |
48 | + ZLMediaKitTaskUtils zlMediaKitTaskUtils, | |
49 | + Map<String, Boolean> startGetMedia) { | |
50 | + this.zlMediaKitRestFulUtils = zlMediaKitRestFulUtils; | |
51 | + this.tkMediaServerNodeService = tkMediaServerNodeService; | |
52 | + this.tkMediaServerService = tkMediaServerService; | |
53 | + this.mediaConfig = mediaConfig; | |
54 | + this.zlMediaKitTaskUtils = zlMediaKitTaskUtils; | |
55 | + this.startGetMedia = startGetMedia; | |
56 | + } | |
57 | + | |
58 | + @PostConstruct | |
59 | + public void init() { | |
60 | + updateDefaultMediaServer(); | |
61 | + // 从数据库获取所有的流媒体信息 | |
62 | + List<MediaServerDTO> dtoList = tkMediaServerService.getAllMediaKit(); | |
63 | + | |
64 | + Optional.ofNullable(dtoList).ifPresent( all ->{ | |
65 | + all.forEach(dto ->{ | |
66 | + String key = dto.getMediaServerId(); | |
67 | + startGetMedia.put(key, true); | |
68 | + zlMediaKitTaskUtils.startCronTask( | |
69 | + key, | |
70 | + () -> { | |
71 | + ZLMediaKitServerConfig zlMediaKitServerConfig = getMediaServerConfig(dto); | |
72 | + if (null != zlMediaKitServerConfig) { | |
73 | + startGetMedia.remove(dto.getMediaServerId()); | |
74 | + zlMediaKitTaskUtils.stop(key); | |
75 | + zlMediaKitServerConfig.setIp(dto.getIp()); | |
76 | + zlMediaKitServerConfig.setHttpPort(dto.getHttpPort()); | |
77 | + zlMediaKitServerConfig.setTenantId(dto.getTenantId()); | |
78 | + // 进行上线操作 | |
79 | + tkMediaServerNodeService.zlmServerOnline(zlMediaKitServerConfig); | |
80 | + } | |
81 | + }, | |
82 | + defaultStateCheckIntervalInSec); | |
83 | + }); | |
84 | + }); | |
85 | + } | |
86 | + | |
87 | + private void updateDefaultMediaServer() { | |
88 | + // 默认流媒体服务器 | |
89 | + MediaServerDTO oldMediaServer = tkMediaServerService.findDefaultMediaServer(); | |
90 | + MediaServerDTO defaultConfig = mediaConfig.getMediaSerItem(); | |
91 | + if (oldMediaServer == null) { | |
92 | + defaultConfig.setTenantId(EntityId.NULL_UUID.toString()); | |
93 | + }else{ | |
94 | + defaultConfig.setId(oldMediaServer.getId()); | |
95 | + defaultConfig.setTenantId(oldMediaServer.getTenantId()); | |
96 | + } | |
97 | + tkMediaServerService.saveOrUpdateMediaServer(defaultConfig); | |
98 | + } | |
99 | + | |
100 | + /** | |
101 | + * 调用流媒体API接口获取流媒体配置 | |
102 | + * @param mediaServerItem | |
103 | + * @return | |
104 | + */ | |
105 | + private ZLMediaKitServerConfig getMediaServerConfig(MediaServerDTO mediaServerItem) { | |
106 | + log.error("启动流媒体【ZLMedia】验证,流媒体编号【{}】",mediaServerItem.getMediaServerId()); | |
107 | + if (startGetMedia == null) { | |
108 | + return null; | |
109 | + } | |
110 | + if (!mediaServerItem.isDefaultServer() | |
111 | + && tkMediaServerNodeService.getOne(mediaServerItem.getMediaServerId()).isEmpty()) { | |
112 | + return null; | |
113 | + } | |
114 | + if (startGetMedia.get(mediaServerItem.getMediaServerId()) == null | |
115 | + || !startGetMedia.get(mediaServerItem.getMediaServerId())) { | |
116 | + return null; | |
117 | + } | |
118 | + JsonNode responseJson = zlMediaKitRestFulUtils.getMediaServerConfig(mediaServerItem); | |
119 | + if(responseJson == null){ | |
120 | + log.error("流媒体【{}】服务地址【{}:{}】无法访问",mediaServerItem.getMediaServerId(),mediaServerItem.getIp(),mediaServerItem.getHttpPort()); | |
121 | + return null; | |
122 | + } | |
123 | + JsonNode data = responseJson.get(FastIotConstants.ZLMediaBody.DATA); | |
124 | + if (data != null && !data.isEmpty()) { | |
125 | + log.error("流媒体【{}:{}】调用成功,响应内容【{}】",mediaServerItem.getIp(),mediaServerItem.getHttpPort(),data); | |
126 | + return JacksonUtil.convertValue(data.get(0), ZLMediaKitServerConfig.class); | |
127 | + } | |
128 | + log.error("流媒体【{}:{}】调用失败,失败原因【{}】",mediaServerItem.getIp(),mediaServerItem.getHttpPort(),responseJson.get(FastIotConstants.ZLMediaBody.MSG)); | |
129 | + return null; | |
130 | + } | |
131 | +} | ... | ... |
1 | +package org.thingsboard.server.utils; | |
2 | + | |
3 | +import org.thingsboard.server.common.data.yunteng.enums.AttributeSourceDataTypeEnum; | |
4 | +import org.thingsboard.server.common.data.yunteng.enums.OperationTypeEnum; | |
5 | + | |
6 | +public class ImportModbusUtils { | |
7 | + | |
8 | + public static Boolean operationTypeAndOriginalDataTypeStatus(String operationType, String OriginalDataType){ | |
9 | + if((operationType.equals(OperationTypeEnum.inputStatus_r_02.name())|| | |
10 | + operationType.equals(OperationTypeEnum.coilStatus_r_01.name())|| | |
11 | + operationType.equals(OperationTypeEnum.coilStatus_rw_01_05.name())|| | |
12 | + operationType.equals(OperationTypeEnum.coilStatus_rw_01_0F.name())|| | |
13 | + operationType.equals(OperationTypeEnum.coilStatus_w_05.name())|| | |
14 | + operationType.equals(OperationTypeEnum.coilStatus_w_0F.name()))&&!OriginalDataType.equals(AttributeSourceDataTypeEnum.BOOLEAN.name())){ | |
15 | + return false; | |
16 | + } | |
17 | + if((operationType.equals(OperationTypeEnum.holdingRegister_rw_03_06.name())|| | |
18 | + operationType.equals(OperationTypeEnum.holdingRegister_w_06.name()))&& | |
19 | + !OriginalDataType.equals(AttributeSourceDataTypeEnum.INT16_AB.name())&& | |
20 | + !OriginalDataType.equals(AttributeSourceDataTypeEnum.INT16_BA.name())&& | |
21 | + !OriginalDataType.equals(AttributeSourceDataTypeEnum.UINT16_AB.name())&& | |
22 | + !OriginalDataType.equals(AttributeSourceDataTypeEnum.UINT16_BA.name())&& | |
23 | + !OriginalDataType.equals(AttributeSourceDataTypeEnum.BOOLEAN.name())&& | |
24 | + !OriginalDataType.equals(AttributeSourceDataTypeEnum.BITS.name()) | |
25 | + ){ | |
26 | + return false; | |
27 | + } | |
28 | + return true; | |
29 | + } | |
30 | + | |
31 | + | |
32 | + public static String getOriginalDataTypeIsDateType(String OriginalDataType){ | |
33 | + if(OriginalDataType.equals(AttributeSourceDataTypeEnum.FLOAT_AB_CD.name())|| | |
34 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.FLOAT_CD_AB.name())|| | |
35 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.FLOAT_BA_DC.name())|| | |
36 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.FLOAT_DC_BA.name())|| | |
37 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.DOUBLE.name())){ | |
38 | + return "DOUBLE"; | |
39 | + } | |
40 | + if(OriginalDataType.equals(AttributeSourceDataTypeEnum.BOOLEAN.name())|| | |
41 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.BITS.name())){ | |
42 | + return "BOOLEAN"; | |
43 | + } | |
44 | + if(OriginalDataType.equals(AttributeSourceDataTypeEnum.STRING.name())){ | |
45 | + return "STRING"; | |
46 | + } | |
47 | + return "INT"; | |
48 | + } | |
49 | + | |
50 | + public static String getOriginalDataTypeIsValueRange(String OriginalDataType){ | |
51 | + if(OriginalDataType.equals(AttributeSourceDataTypeEnum.INT16_AB.name())|| | |
52 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.INT16_BA.name())){ | |
53 | + return "\"valueRange\":{\"min\":-32768,\"max\":32767}"; | |
54 | + } | |
55 | + if(OriginalDataType.equals(AttributeSourceDataTypeEnum.UINT16_AB.name())|| | |
56 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.UINT16_BA.name())){ | |
57 | + return "\"valueRange\":{\"min\":0,\"max\":65535}"; | |
58 | + } | |
59 | + if(OriginalDataType.equals(AttributeSourceDataTypeEnum.UINT32_AB_CD.name())|| | |
60 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.UINT32_CD_AB.name())|| | |
61 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.UINT32_BA_DC.name())|| | |
62 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.UINT32_DC_BA.name())){ | |
63 | + return "\"valueRange\":{\"min\":0,\"max\":4294967295}"; | |
64 | + } | |
65 | + if(OriginalDataType.equals(AttributeSourceDataTypeEnum.STRING.name())){ | |
66 | + return ""; | |
67 | + } | |
68 | + if(OriginalDataType.equals(AttributeSourceDataTypeEnum.BOOLEAN.name())|| | |
69 | + OriginalDataType.equals(AttributeSourceDataTypeEnum.BITS.name())){ | |
70 | + return "\"valueRange\":{\"min\":0,\"max\":1}"; | |
71 | + } | |
72 | + return "\"valueRange\":{\"min\":-2147483648,\"max\":2147483647}"; | |
73 | + } | |
74 | + | |
75 | + public static String getOperationTypeIsRw(String operationType){ | |
76 | + String []rw = operationType.split("_"); | |
77 | + return rw[1].equals("r")?"r":"rw"; | |
78 | + } | |
79 | + | |
80 | + public static Boolean getOperationTypeIsWriteOnly(String operationType){ | |
81 | + String rw = getOperationTypeIsRw(operationType); | |
82 | + if(rw.equals("r")){//只读为false | |
83 | + return false; | |
84 | + } | |
85 | + return true; | |
86 | + } | |
87 | +} | ... | ... |
No preview for this file type
... | ... | @@ -451,6 +451,12 @@ caffeine: |
451 | 451 | sceneReact: |
452 | 452 | timeToLiveInMinutes: "${CACHE_SPECS_SCENE_TTL:1440}" |
453 | 453 | maxSize: "${CACHE_SPECS_SCENE_MAX_SIZE:10000}" |
454 | + tkSipCacheName: | |
455 | + timeToLiveInMinutes: "${CACHE_SPECS_TK_SIP_TTL:1440}" | |
456 | + maxSize: "${CACHE_SPECS_TK_SIP_MAX_SIZE:10000}" | |
457 | + tkMediaServerCacheName: | |
458 | + timeToLiveInMinutes: "${CACHE_SPECS_TK_MEDIA_SERVER_TTL:1440}" | |
459 | + maxSize: "${CACHE_SPECS_TK_MEDIA_SERVER_MAX_SIZE:10000}" | |
454 | 460 | redis: |
455 | 461 | # standalone or cluster |
456 | 462 | connection: |
... | ... | @@ -476,7 +482,7 @@ redis: |
476 | 482 | # db index |
477 | 483 | db: "${REDIS_DB:0}" |
478 | 484 | # db password |
479 | - password: "${REDIS_PASSWORD:}" | |
485 | + password: "${REDIS_PASSWORD:redis@6379}" | |
480 | 486 | # pool config |
481 | 487 | pool_config: |
482 | 488 | maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}" |
... | ... | @@ -712,6 +718,9 @@ transport: |
712 | 718 | # Skip certificate validity check for client certificates. |
713 | 719 | skip_validity_check_for_client_cert: "${MQTT_SSL_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}" |
714 | 720 | # Local CoAP transport parameters |
721 | + gbt28181: | |
722 | + # Enable/disable gbt28181 transport protocol. | |
723 | + enabled: "${GBT28181_ENABLED:false}" | |
715 | 724 | tcp: |
716 | 725 | # Enable/disable tcp transport protocol. |
717 | 726 | enabled: "${TCP_ENABLED:true}" |
... | ... | @@ -1218,3 +1227,45 @@ logging: |
1218 | 1227 | frp: |
1219 | 1228 | server: |
1220 | 1229 | address: ${FRP_SERVER_ADDRESS:http://127.0.0.1} |
1230 | +sip: | |
1231 | + # [必须修改] 本机的IP,对应你的网卡,监听什么ip就是使用什么网卡, | |
1232 | + # 如果要监听多张网卡,可以使用逗号分隔多个IP, 例如: 192.168.1.4,10.0.0.4 | |
1233 | + # 如果不明白,就使用0.0.0.0,大部分情况都是可以的 | |
1234 | + # 请不要使用127.0.0.1,任何包括localhost在内的域名都是不可以的。 | |
1235 | + ip: ${GBT28181_SIP_IP:127.0.0.1} | |
1236 | + # [可选] 28181服务监听的端口 | |
1237 | + port: ${GBT28181_SIP_PORT:5060} | |
1238 | + #[可选] | |
1239 | + id: ${GBT28181_SIP_ID:51010700599000000001} | |
1240 | + # 根据国标6.1.2中规定,domain宜采用ID统一编码的前十位编码。国标附录D中定义前8位为中心编码(由省级、市级、区级、基层编号组成,参照GB/T 2260-2007) | |
1241 | + # 后两位为行业编码,定义参照附录D.3 | |
1242 | + # 标识四川成都武侯下区 信息行业接入 | |
1243 | + # [可选] | |
1244 | + domain: ${GBT28181_SIP_DOMAIN:5101070059} | |
1245 | + #[可选] | |
1246 | + password: ${GBT28181_SIP_PASSWORD:61332286} | |
1247 | +#zlm 默认服务器配置 | |
1248 | +media: | |
1249 | + id: ${GBT28181_MEDIA_GENERAL_ID:D2okJWKKaQ5bX7Va} | |
1250 | + # [必须修改] zlm服务器的内网IP | |
1251 | + ip: ${GBT28181_MEDIA_IP:127.0.0.1} | |
1252 | + # [必须修改] zlm服务器的http.port | |
1253 | + http-port: ${GBT28181_MEDIA_HTTP_PORT:28080} | |
1254 | + # [可选] zlm服务器的hook.admin_params=secret | |
1255 | + secret: ${GBT28181_MEDIA_API_SECRET:QhrTN7k6HcDnt0YyeolwHwiVYDgIHPMZ} | |
1256 | + # 启用多端口模式, 多端口模式使用端口区分每路流,兼容性更好。 单端口使用流的ssrc区分, 点播超时建议使用多端口测试 | |
1257 | + rtp: | |
1258 | + # [可选] 是否启用多端口模式, 开启后会在portRange范围内选择端口用于媒体流传输 | |
1259 | + enable: true | |
1260 | + # [可选] 在此范围内选择端口用于媒体流传输, 必须提前在zlm上配置该属性,不然自动配置此属性可能不成功 | |
1261 | + port-range: ${GBT28181_MEDIA_RTP_PORT_RANGE:30000,30500} # 端口范围 | |
1262 | + # [可选] 国标级联在此范围内选择端口发送媒体流, | |
1263 | + send-port-range: ${GBT28181_MEDIA_RTP_PORT_RANGE:30000,30500} # 端口范围 | |
1264 | + # 录像辅助服务, 部署此服务可以实现zlm录像的管理与下载, 0 表示不使用 | |
1265 | + record-assist-port: 0 | |
1266 | + defaultStateCheckIntervalInSec: "${DEFAULT_STATE_CHECK_INTERVAL:10}" | |
1267 | + | |
1268 | +thingskit: | |
1269 | + release: | |
1270 | + version: v1.1.1 Release | |
1271 | + date: 20230630 | ... | ... |
... | ... | @@ -8,7 +8,7 @@ |
8 | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | 9 | * |
10 | 10 | * Unless required by applicable law or agreed to in writing, software |
11 | - * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS,in | |
12 | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
... | ... | @@ -130,10 +130,7 @@ message SessionEventMsg { |
130 | 130 | SessionEvent event = 2; |
131 | 131 | } |
132 | 132 | |
133 | -//thingskit | |
134 | -message PostEventMsg { | |
135 | - repeated KeyValueProto kv = 1; | |
136 | -} | |
133 | + | |
137 | 134 | message PostTelemetryMsg { |
138 | 135 | repeated TsKvListProto tsKvList = 1; |
139 | 136 | } |
... | ... | @@ -682,6 +679,7 @@ message TransportApiRequestMsg { |
682 | 679 | |
683 | 680 | //Thingskit function |
684 | 681 | ScriptProto script = 14; |
682 | + Gbt28181RequestMsg gbt28181RequestMsg = 15; | |
685 | 683 | } |
686 | 684 | |
687 | 685 | /* Response from ThingsBoard Core Service to Transport Service */ |
... | ... | @@ -699,6 +697,7 @@ message TransportApiResponseMsg { |
699 | 697 | |
700 | 698 | //Thingskit function |
701 | 699 | repeated ScriptProto scriptsResponseMsg = 11; |
700 | + Gbt28181ResponseMsg gbt28181ResponseMsg = 12; | |
702 | 701 | } |
703 | 702 | |
704 | 703 | /* Messages that are handled by ThingsBoard Core Service */ |
... | ... | @@ -795,3 +794,37 @@ message ScriptProto{ |
795 | 794 | string convertJs = 6; |
796 | 795 | int32 status = 7; |
797 | 796 | } |
797 | +//thingskit function 设备上报的物模型事件 | |
798 | +message PostEventMsg { | |
799 | + repeated KeyValueProto kv = 1; | |
800 | +} | |
801 | +message Gbt28181RequestMsg{ | |
802 | + bytes context = 1; | |
803 | + string contextType = 2; | |
804 | + string callId = 3; | |
805 | + string fromTag = 4; | |
806 | + string toTag = 5; | |
807 | + string viaTag = 6; | |
808 | + string viaBranch = 7; | |
809 | + int32 sn = 8; | |
810 | + int64 tenantIdMSB = 9; | |
811 | + int64 tenantIdLSB = 10; | |
812 | + int64 entityIdMSB = 11; | |
813 | + int64 entityIdLSB = 12; | |
814 | +} | |
815 | +message Gbt28181ResponseMsg{ | |
816 | + bytes context = 1; | |
817 | + string contextType = 2; | |
818 | + string callId = 3; | |
819 | + string fromTag = 4; | |
820 | + string toTag = 5; | |
821 | + string viaTag = 6; | |
822 | + string viaBranch = 7; | |
823 | + int32 sn = 8; | |
824 | + int64 tenantIdMSB = 9; | |
825 | + int64 tenantIdLSB = 10; | |
826 | + int64 entityIdMSB = 11; | |
827 | + int64 entityIdLSB = 12; | |
828 | +} | |
829 | + | |
830 | + | ... | ... |
... | ... | @@ -48,6 +48,8 @@ public class DataConstants { |
48 | 48 | public static final String HTTP_TRANSPORT_NAME = "HTTP"; |
49 | 49 | public static final String SNMP_TRANSPORT_NAME = "SNMP"; |
50 | 50 | |
51 | + public static final String GBT_28181 = "GBT28181"; | |
52 | + | |
51 | 53 | |
52 | 54 | public static final String[] allScopes() { |
53 | 55 | return new String[]{CLIENT_SCOPE, SHARED_SCOPE, SERVER_SCOPE}; | ... | ... |
... | ... | @@ -34,6 +34,7 @@ import java.io.Serializable; |
34 | 34 | @JsonSubTypes.Type(value = DefaultDeviceTransportConfiguration.class, name = "DEFAULT"), |
35 | 35 | @JsonSubTypes.Type(value = MqttDeviceTransportConfiguration.class, name = "MQTT"), |
36 | 36 | @JsonSubTypes.Type(value = TkTcpDeviceTransportConfiguration.class, name = "TCP"), |
37 | + @JsonSubTypes.Type(value = TkGBTDeviceTransportConfiguration.class, name = "GBT28181"), | |
37 | 38 | @JsonSubTypes.Type(value = CoapDeviceTransportConfiguration.class, name = "COAP"), |
38 | 39 | @JsonSubTypes.Type(value = Lwm2mDeviceTransportConfiguration.class, name = "LWM2M"), |
39 | 40 | @JsonSubTypes.Type(value = SnmpDeviceTransportConfiguration.class, name = "SNMP")}) | ... | ... |
1 | +/** | |
2 | + * Copyright © 2016-2022 The Thingsboard Authors | |
3 | + * | |
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | + * you may not use this file except in compliance with the License. | |
6 | + * You may obtain a copy of the License at | |
7 | + * | |
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | + * | |
10 | + * Unless required by applicable law or agreed to in writing, software | |
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | + * See the License for the specific language governing permissions and | |
14 | + * limitations under the License. | |
15 | + */ | |
16 | +package org.thingsboard.server.common.data.device.data; | |
17 | +import lombok.Data; | |
18 | +import org.thingsboard.server.common.data.DeviceTransportType; | |
19 | + | |
20 | +@Data | |
21 | +public class TkGBTDeviceTransportConfiguration implements DeviceTransportConfiguration { | |
22 | + @Override | |
23 | + public DeviceTransportType getType() { | |
24 | + return DeviceTransportType.GBT28181; | |
25 | + } | |
26 | + | |
27 | +} | ... | ... |
... | ... | @@ -32,6 +32,7 @@ import java.io.Serializable; |
32 | 32 | @JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"), |
33 | 33 | @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"), |
34 | 34 | @JsonSubTypes.Type(value = TkTcpDeviceProfileTransportConfiguration.class, name = "TCP"), |
35 | + @JsonSubTypes.Type(value = TkGBT28181DeviceProfileTransportConfiguration.class, name = "GBT28181"), | |
35 | 36 | @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M"), |
36 | 37 | @JsonSubTypes.Type(value = CoapDeviceProfileTransportConfiguration.class, name = "COAP"), |
37 | 38 | @JsonSubTypes.Type(value = SnmpDeviceProfileTransportConfiguration.class, name = "SNMP") | ... | ... |
1 | +package org.thingsboard.server.common.data.device.profile; | |
2 | + | |
3 | +import lombok.Data; | |
4 | +import org.thingsboard.server.common.data.DeviceTransportType; | |
5 | + | |
6 | +@Data | |
7 | +public class TkGBT28181DeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { | |
8 | + @Override | |
9 | + public DeviceTransportType getType() { | |
10 | + return DeviceTransportType.GBT28181; | |
11 | + } | |
12 | + | |
13 | + | |
14 | +} | ... | ... |
... | ... | @@ -3,6 +3,7 @@ package org.thingsboard.server.common.data.device.profile; |
3 | 3 | import lombok.Data; |
4 | 4 | import org.thingsboard.server.common.data.DeviceTransportType; |
5 | 5 | import org.thingsboard.server.common.data.validation.NoXss; |
6 | +import org.thingsboard.server.common.data.yunteng.enums.ProtocolAnalysisEnum; | |
6 | 7 | |
7 | 8 | @Data |
8 | 9 | public class TkTcpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { |
... | ... | @@ -26,6 +27,12 @@ public class TkTcpDeviceProfileTransportConfiguration implements DeviceProfileTr |
26 | 27 | @NoXss |
27 | 28 | private String downScriptId; |
28 | 29 | |
30 | + /** | |
31 | + * 协议: 默认自定义 | |
32 | + */ | |
33 | + @NoXss | |
34 | + private ProtocolAnalysisEnum protocol = ProtocolAnalysisEnum.CUSTOM; | |
35 | + | |
29 | 36 | @Override |
30 | 37 | public DeviceTransportType getType() { |
31 | 38 | return DeviceTransportType.TCP; | ... | ... |
1 | 1 | package org.thingsboard.server.common.data.yunteng.common; |
2 | 2 | |
3 | 3 | import org.thingsboard.server.common.data.yunteng.dto.SysDictItemDTO; |
4 | +import org.thingsboard.server.common.data.yunteng.dto.sip.MediaServerDTO; | |
4 | 5 | |
5 | 6 | public interface TkCommonService { |
6 | 7 | /** |
... | ... | @@ -11,6 +12,4 @@ public interface TkCommonService { |
11 | 12 | * @return 返回字典Item表 |
12 | 13 | */ |
13 | 14 | SysDictItemDTO getDictValueByCodeAndText(String dictCode, String codeValue); |
14 | - | |
15 | - | |
16 | 15 | } | ... | ... |
1 | +package org.thingsboard.server.common.data.yunteng.common.media; | |
2 | + | |
3 | +import java.util.*; | |
4 | +import lombok.extern.slf4j.Slf4j; | |
5 | +import org.springframework.beans.factory.annotation.Autowired; | |
6 | +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; | |
7 | +import org.springframework.stereotype.Component; | |
8 | +import org.thingsboard.server.common.data.yunteng.config.media.UserSetting; | |
9 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | |
10 | +import org.thingsboard.server.common.data.yunteng.core.cache.CacheUtils; | |
11 | +import org.thingsboard.server.common.data.yunteng.dto.sip.SipMessageHeaderDTO; | |
12 | +import org.thingsboard.server.common.data.yunteng.dto.sip.SsrcTransactionDTO; | |
13 | +import org.thingsboard.server.common.data.yunteng.enums.SessionTypeEnum; | |
14 | + | |
15 | +/** 视频流session管理器,管理视频预览、预览回放的通信句柄 */ | |
16 | +@Component | |
17 | +@Slf4j | |
18 | +public class VideoStreamSessionManager { | |
19 | + | |
20 | + @Autowired private UserSetting userSetting; | |
21 | + @Autowired private CacheUtils cacheUtils; | |
22 | + private final String cacheName = FastIotConstants.MediaServerKey.MEDIA_SERVER_CACHE_NAME; | |
23 | + | |
24 | + /** | |
25 | + * 添加一个点播/回放的事务信息 后续可以通过流Id/callID | |
26 | + * | |
27 | + * @param cameraCode 设备ID | |
28 | + * @param channelId 通道ID | |
29 | + * @param callId 一次请求的CallID | |
30 | + * @param stream 流名称 | |
31 | + * @param mediaServerId 所使用的流媒体ID | |
32 | + */ | |
33 | + public void put( | |
34 | + String cameraCode, | |
35 | + String channelId, | |
36 | + String callId, | |
37 | + String stream, | |
38 | + String ssrc, | |
39 | + SipMessageHeaderDTO sipMessage, | |
40 | + String mediaServerId, | |
41 | + SessionTypeEnum type) { | |
42 | + | |
43 | + SsrcTransactionDTO ssrcTransaction = new SsrcTransactionDTO(); | |
44 | + ssrcTransaction.setCameraCode(cameraCode); | |
45 | + ssrcTransaction.setChannelId(channelId); | |
46 | + ssrcTransaction.setStream(stream); | |
47 | + ssrcTransaction.setSipTransactionInfo(sipMessage); | |
48 | + ssrcTransaction.setCallId(callId); | |
49 | + ssrcTransaction.setSsrc(ssrc); | |
50 | + ssrcTransaction.setMediaServerId(mediaServerId); | |
51 | + ssrcTransaction.setType(type); | |
52 | + String fullKey = buildTransactionFullKey(cameraCode, channelId, callId, stream); | |
53 | + String channelKey = buildTransactionChannelKey(cameraCode, channelId, stream); | |
54 | + String streamKey = buildTransactionStreamKey(stream); | |
55 | + String cameraKey = buildTransactionCameraKey(cameraCode); | |
56 | + | |
57 | + freshTransactionKey(cameraKey, fullKey, false); | |
58 | + freshTransactionKey(streamKey, fullKey, false); | |
59 | + freshTransactionKey(channelKey, fullKey, false); | |
60 | + cacheUtils.put(cacheName, fullKey, ssrcTransaction); | |
61 | + } | |
62 | + | |
63 | + private void freshTransactionKey(String key, String val, boolean remove) { | |
64 | + Optional<Set<String>> cameraDatas = cacheUtils.get(cacheName, key); | |
65 | + Set<String> values = new HashSet<>(); | |
66 | + cameraDatas.ifPresent(values::addAll); | |
67 | + if (remove) { | |
68 | + values.remove(val); | |
69 | + } else { | |
70 | + values.add(val); | |
71 | + } | |
72 | + cacheUtils.put(cacheName, key, values); | |
73 | + } | |
74 | + | |
75 | + public Optional<Set<String>> getSsrcTransactionCamaraKey(String cameraCode) { | |
76 | + return cacheUtils.get(cacheName, buildTransactionCameraKey(cameraCode)); | |
77 | + } | |
78 | + | |
79 | + public Optional<Set<String>> getSsrcTransactionStreamKey(String stream) { | |
80 | + return cacheUtils.get(cacheName, buildTransactionStreamKey(stream)); | |
81 | + } | |
82 | + | |
83 | + public Optional<Set<String>> getSsrcTransactionChannelKey( | |
84 | + String cameraCode, String channelId, String stream) { | |
85 | + return cacheUtils.get(cacheName, buildTransactionChannelKey(cameraCode, channelId, stream)); | |
86 | + } | |
87 | + | |
88 | + public Optional<SsrcTransactionDTO> getSsrcTransaction(String fullKey) { | |
89 | + return cacheUtils.get(cacheName, fullKey); | |
90 | + } | |
91 | + public Optional<SsrcTransactionDTO> getSsrcTransaction(String cameraCode, String channelId, String stream) { | |
92 | + Optional<Set<String>> keys =getSsrcTransactionChannelKey(cameraCode, channelId,stream); | |
93 | + | |
94 | + if(keys.isPresent()){ | |
95 | + Optional<String> fullKey =keys.get().stream().findFirst(); | |
96 | + if(fullKey.isPresent()){ | |
97 | + return getSsrcTransaction(fullKey.get()); | |
98 | + } | |
99 | + } | |
100 | + return Optional.empty(); | |
101 | + } | |
102 | + | |
103 | + public String getMediaServerId(String deviceId, String channelId, String stream) { | |
104 | + Optional<Set<String>> channels = getSsrcTransactionChannelKey(deviceId, channelId, stream); | |
105 | + if (channels.isPresent()) { | |
106 | + for (String fullKey : channels.get()) { | |
107 | + Optional<SsrcTransactionDTO> result = getSsrcTransaction(fullKey); | |
108 | + if (result.isPresent()) { | |
109 | + return result.get().getMediaServerId(); | |
110 | + } | |
111 | + } | |
112 | + } | |
113 | + return null; | |
114 | + } | |
115 | + | |
116 | + public String getSSRC(String deviceId, String channelId, String stream) { | |
117 | + Optional<Set<String>> channels = getSsrcTransactionChannelKey(deviceId, channelId, stream); | |
118 | + | |
119 | + if (channels.isPresent()) { | |
120 | + for (String fullKey : channels.get()) { | |
121 | + Optional<SsrcTransactionDTO> result = getSsrcTransaction(fullKey); | |
122 | + if (result.isPresent()) { | |
123 | + return result.get().getSsrc(); | |
124 | + } | |
125 | + } | |
126 | + } | |
127 | + return null; | |
128 | + } | |
129 | + | |
130 | + public void remove(String cameraCode, String channelId, String stream) { | |
131 | + Optional<Set<String>> channelKeys = getSsrcTransactionChannelKey(cameraCode, channelId, stream); | |
132 | + channelKeys.ifPresent( | |
133 | + keys -> { | |
134 | + keys.forEach( | |
135 | + fullKey -> { | |
136 | + String streamKey = buildTransactionStreamKey(stream); | |
137 | + String cameraKey = buildTransactionCameraKey(cameraCode); | |
138 | + String channelKey = buildTransactionChannelKey(cameraCode, channelId, stream); | |
139 | + freshTransactionKey(cameraKey, fullKey, true); | |
140 | + freshTransactionKey(streamKey, fullKey, true); | |
141 | + freshTransactionKey(channelKey, fullKey, true); | |
142 | + cacheUtils.invalidate(cacheName, fullKey); | |
143 | + }); | |
144 | + }); | |
145 | + } | |
146 | + | |
147 | + public String buildTransactionFullKey( | |
148 | + String cameraCode, String channelId, String callId, String stream) { | |
149 | + return FastIotConstants.MediaServerKey.MEDIA_TRANSACTION_USED_PREFIX | |
150 | + + userSetting.getServerId() | |
151 | + + "_" | |
152 | + + cameraCode | |
153 | + + "_" | |
154 | + + channelId | |
155 | + + "_" | |
156 | + + callId | |
157 | + + "_" | |
158 | + + stream; | |
159 | + } | |
160 | + | |
161 | + public String buildTransactionChannelKey(String cameraCode, String channelId, String stream) { | |
162 | + return FastIotConstants.MediaServerKey.MEDIA_TRANSACTION_USED_PREFIX | |
163 | + + userSetting.getServerId() | |
164 | + + "_" | |
165 | + + cameraCode | |
166 | + + "_" | |
167 | + + channelId | |
168 | + + "_" | |
169 | + + stream; | |
170 | + } | |
171 | + | |
172 | + public String buildTransactionStreamKey(String stream) { | |
173 | + return FastIotConstants.MediaServerKey.MEDIA_TRANSACTION_USED_PREFIX | |
174 | + + userSetting.getServerId() | |
175 | + + "_" | |
176 | + + stream; | |
177 | + } | |
178 | + | |
179 | + public String buildTransactionCameraKey(String cameraCode) { | |
180 | + return FastIotConstants.MediaServerKey.MEDIA_TRANSACTION_USED_PREFIX | |
181 | + + userSetting.getServerId() | |
182 | + + "_" | |
183 | + + cameraCode; | |
184 | + } | |
185 | + | |
186 | + public String getReceiveRtpKey(String ssrcStream) { | |
187 | + return FastIotConstants.CacheSipKey.TK_OTHER_RECEIVE_RTP_INFO | |
188 | + + userSetting.getServerId() | |
189 | + + "_" | |
190 | + + ssrcStream; | |
191 | + } | |
192 | + | |
193 | + public String getReceivePsKey(String ssrcStream) { | |
194 | + return FastIotConstants.CacheSipKey.TK_OTHER_RECEIVE_PS_INFO | |
195 | + + userSetting.getServerId() | |
196 | + + "_" | |
197 | + + ssrcStream; | |
198 | + } | |
199 | +} | ... | ... |
1 | +package org.thingsboard.server.common.data.yunteng.common.media; | |
2 | + | |
3 | +import java.time.Instant; | |
4 | +import java.time.LocalDateTime; | |
5 | +import java.time.ZoneId; | |
6 | +import java.util.*; | |
7 | +import java.util.concurrent.ConcurrentHashMap; | |
8 | +import java.util.concurrent.TimeUnit; | |
9 | + | |
10 | +import com.fasterxml.jackson.databind.JsonNode; | |
11 | +import com.fasterxml.jackson.databind.node.ObjectNode; | |
12 | +import org.springframework.scheduling.annotation.Scheduled; | |
13 | +import org.springframework.stereotype.Component; | |
14 | +import org.springframework.util.CollectionUtils; | |
15 | +import org.thingsboard.server.common.data.yunteng.dto.sip.MediaServerDTO; | |
16 | +import org.thingsboard.server.common.data.yunteng.dto.sip.hook.IHookSubscribe; | |
17 | +import org.thingsboard.server.common.data.yunteng.enums.HookTypeEnum; | |
18 | + | |
19 | +/** | |
20 | + * ZLMediaServer的hook事件订阅 | |
21 | + * | |
22 | + * @author lin | |
23 | + */ | |
24 | +@Component | |
25 | +public class ZlmHttpHookSubscribe { | |
26 | + | |
27 | + @FunctionalInterface | |
28 | + public interface Event { | |
29 | + void response(MediaServerDTO mediaServerInfo, JsonNode response); | |
30 | + } | |
31 | + | |
32 | + private Map<HookTypeEnum, Map<IHookSubscribe, Event>> allSubscribes = new ConcurrentHashMap<>(); | |
33 | + | |
34 | + public void addSubscribe(IHookSubscribe hookSubscribe, Event event) { | |
35 | + if (hookSubscribe.getExpires() == null) { | |
36 | + // 默认5分钟过期 | |
37 | + Instant expiresInstant = Instant.now().plusSeconds(TimeUnit.MINUTES.toSeconds(5)); | |
38 | + hookSubscribe.setExpires(LocalDateTime.ofInstant(expiresInstant, ZoneId.systemDefault())); | |
39 | + } | |
40 | + allSubscribes | |
41 | + .computeIfAbsent(hookSubscribe.getHookType(), k -> new ConcurrentHashMap<>()) | |
42 | + .put(hookSubscribe, event); | |
43 | + } | |
44 | + | |
45 | + public Event sendNotify(HookTypeEnum type, JsonNode hookResponse) { | |
46 | + Event event = null; | |
47 | + Map<IHookSubscribe, Event> eventMap = allSubscribes.get(type); | |
48 | + if (eventMap == null) { | |
49 | + return null; | |
50 | + } | |
51 | + for (IHookSubscribe key : eventMap.keySet()) { | |
52 | + Boolean result = null; | |
53 | + // 使用 fields() 方法获取键值对迭代器 | |
54 | + Iterator<Map.Entry<String, JsonNode>> fieldsIterator = key.getContent().fields(); | |
55 | + while (fieldsIterator.hasNext()) { | |
56 | + Map.Entry<String, JsonNode> field = fieldsIterator.next(); | |
57 | + String s = field.getKey(); | |
58 | + JsonNode value = field.getValue(); | |
59 | + if (result == null) { | |
60 | + result = value.equals(hookResponse.get(s)); | |
61 | + } else { | |
62 | + if (value == null) { | |
63 | + continue; | |
64 | + } | |
65 | + result = result && value.equals(hookResponse.get(s)); | |
66 | + } | |
67 | + } | |
68 | + if (null != result && result) { | |
69 | + event = eventMap.get(key); | |
70 | + } | |
71 | + } | |
72 | + return event; | |
73 | + } | |
74 | + | |
75 | + public void removeSubscribe(IHookSubscribe hookSubscribe) { | |
76 | + Map<IHookSubscribe, Event> eventMap = allSubscribes.get(hookSubscribe.getHookType()); | |
77 | + if (eventMap == null) { | |
78 | + return; | |
79 | + } | |
80 | + | |
81 | + Set<Map.Entry<IHookSubscribe, Event>> entries = eventMap.entrySet(); | |
82 | + if (entries.size() > 0) { | |
83 | + List<Map.Entry<IHookSubscribe, Event>> entriesToRemove = new ArrayList<>(); | |
84 | + for (Map.Entry<IHookSubscribe, Event> entry : entries) { | |
85 | + ObjectNode content = entry.getKey().getContent(); | |
86 | + if (content == null || content.size() == 0) { | |
87 | + entriesToRemove.add(entry); | |
88 | + continue; | |
89 | + } | |
90 | + Boolean result = null; | |
91 | + Iterator<Map.Entry<String, JsonNode>> fieldsIterator = content.fields(); | |
92 | + while (fieldsIterator.hasNext()) { | |
93 | + Map.Entry<String, JsonNode> field = fieldsIterator.next(); | |
94 | + String s = field.getKey(); | |
95 | + JsonNode value = field.getValue(); | |
96 | + if (result == null) { | |
97 | + result = value.equals(hookSubscribe.getContent().get(s)); | |
98 | + } else { | |
99 | + if (value == null) { | |
100 | + continue; | |
101 | + } | |
102 | + result = result && value.equals(hookSubscribe.getContent().get(s)); | |
103 | + } | |
104 | + } | |
105 | + if (result) { | |
106 | + entriesToRemove.add(entry); | |
107 | + } | |
108 | + } | |
109 | + | |
110 | + if (!CollectionUtils.isEmpty(entriesToRemove)) { | |
111 | + for (Map.Entry<IHookSubscribe, Event> entry : entriesToRemove) { | |
112 | + entries.remove(entry); | |
113 | + } | |
114 | + } | |
115 | + } | |
116 | + } | |
117 | + | |
118 | + /** | |
119 | + * 获取某个类型的所有的订阅 | |
120 | + * | |
121 | + * @param type | |
122 | + * @return | |
123 | + */ | |
124 | + public List<Event> getSubscribes(HookTypeEnum type) { | |
125 | + Map<IHookSubscribe, Event> eventMap = allSubscribes.get(type); | |
126 | + if (eventMap == null) { | |
127 | + return null; | |
128 | + } | |
129 | + List<Event> result = new ArrayList<>(); | |
130 | + for (IHookSubscribe key : eventMap.keySet()) { | |
131 | + result.add(eventMap.get(key)); | |
132 | + } | |
133 | + return result; | |
134 | + } | |
135 | + | |
136 | + public List<IHookSubscribe> getAll() { | |
137 | + ArrayList<IHookSubscribe> result = new ArrayList<>(); | |
138 | + Collection<Map<IHookSubscribe, Event>> values = allSubscribes.values(); | |
139 | + for (Map<IHookSubscribe, Event> value : values) { | |
140 | + result.addAll(value.keySet()); | |
141 | + } | |
142 | + return result; | |
143 | + } | |
144 | + | |
145 | + /** 对订阅数据进行过期清理 */ | |
146 | + @Scheduled(cron = "0 0/5 * * * ?") // 每5分钟执行一次 | |
147 | + public void execute() { | |
148 | + | |
149 | + Instant instant = Instant.now().minusMillis(TimeUnit.MINUTES.toMillis(5)); | |
150 | + LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); | |
151 | + int total = 0; | |
152 | + for (HookTypeEnum hookType : allSubscribes.keySet()) { | |
153 | + Map<IHookSubscribe, Event> hookSubscribeEventMap = allSubscribes.get(hookType); | |
154 | + if (hookSubscribeEventMap.size() > 0) { | |
155 | + for (IHookSubscribe hookSubscribe : hookSubscribeEventMap.keySet()) { | |
156 | + if (hookSubscribe.getExpires().isBefore(localDateTime)) { | |
157 | + // 过期的 | |
158 | + hookSubscribeEventMap.remove(hookSubscribe); | |
159 | + total++; | |
160 | + } | |
161 | + } | |
162 | + } | |
163 | + } | |
164 | + } | |
165 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/config/media/MediaConfig.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.config.media; | |
2 | + | |
3 | +import lombok.Data; | |
4 | +import lombok.extern.slf4j.Slf4j; | |
5 | +import org.springframework.beans.factory.annotation.Value; | |
6 | +import org.springframework.boot.context.properties.ConfigurationProperties; | |
7 | +import org.springframework.stereotype.Component; | |
8 | +import org.springframework.util.ObjectUtils; | |
9 | +import org.thingsboard.server.common.data.yunteng.dto.sip.MediaServerDTO; | |
10 | + | |
11 | +import java.net.InetAddress; | |
12 | +import java.net.UnknownHostException; | |
13 | +import java.util.Objects; | |
14 | +import java.util.regex.Pattern; | |
15 | + | |
16 | +/** | |
17 | + * 配置文件里面的流媒体配置信息 | |
18 | + */ | |
19 | +@ConfigurationProperties(prefix = "media") | |
20 | +@Component | |
21 | +@Data | |
22 | +@Slf4j | |
23 | +public class MediaConfig { | |
24 | + @Value("${media.id}") | |
25 | + private String mediaServerId; | |
26 | + | |
27 | + @Value("${media.ip}") | |
28 | + private String ip; | |
29 | + | |
30 | + @Value("${media.hook-ip:}") | |
31 | + private String hookIp; | |
32 | + | |
33 | + @Value("${sip.ip}") | |
34 | + private String sipIp; | |
35 | + | |
36 | + @Value("${sip.domain}") | |
37 | + private String sipDomain; | |
38 | + | |
39 | + @Value("${media.sdp-ip:${media.ip}}") | |
40 | + private String sdpIp; | |
41 | + | |
42 | + @Value("${media.stream-ip:${media.ip}}") | |
43 | + private String streamIp; | |
44 | + | |
45 | + @Value("${media.http-port}") | |
46 | + private Integer httpPort; | |
47 | + | |
48 | + @Value("${media.http-ssl-port:0}") | |
49 | + private Integer httpSslPort = 0; | |
50 | + | |
51 | + @Value("${media.rtmp-port:0}") | |
52 | + private Integer rtmpPort = 0; | |
53 | + | |
54 | + @Value("${media.rtmp-ssl-port:0}") | |
55 | + private Integer rtmpSslPort = 0; | |
56 | + | |
57 | + @Value("${media.rtp-proxy-port:0}") | |
58 | + private Integer rtpProxyPort = 0; | |
59 | + | |
60 | + @Value("${media.rtsp-port:0}") | |
61 | + private Integer rtspPort = 0; | |
62 | + | |
63 | + @Value("${media.rtsp-ssl-port:0}") | |
64 | + private Integer rtspSslPort = 0; | |
65 | + | |
66 | + @Value("${media.auto-config:true}") | |
67 | + private boolean autoConfig = true; | |
68 | + | |
69 | + @Value("${media.secret}") | |
70 | + private String secret; | |
71 | + | |
72 | + @Value("${media.rtp.enable}") | |
73 | + private boolean rtpEnable; | |
74 | + | |
75 | + @Value("${media.rtp.port-range}") | |
76 | + private String rtpPortRange; | |
77 | + | |
78 | + @Value("${media.rtp.send-port-range}") | |
79 | + private String rtpSendPortRange; | |
80 | + | |
81 | + @Value("${media.record-assist-port:0}") | |
82 | + private Integer recordAssistPort = 0; | |
83 | + private boolean status; | |
84 | + | |
85 | + public MediaServerDTO getMediaSerItem() { | |
86 | + MediaServerDTO mediaServerItem = new MediaServerDTO(); | |
87 | + mediaServerItem.setMediaServerId(mediaServerId); | |
88 | + mediaServerItem.setIp(ip); | |
89 | + mediaServerItem.setDefaultServer(true); | |
90 | + mediaServerItem.setHookIp(getHookIp()); | |
91 | + mediaServerItem.setSdpIp(getSdpIp()); | |
92 | + mediaServerItem.setStreamIp(getStreamIp()); | |
93 | + mediaServerItem.setHttpPort(httpPort); | |
94 | + mediaServerItem.setHttpSslPort(httpSslPort); | |
95 | + mediaServerItem.setRtmpPort(rtmpPort); | |
96 | + mediaServerItem.setRtmpSslPort(rtmpSslPort); | |
97 | + mediaServerItem.setRtpProxyPort(getRtpProxyPort()); | |
98 | + mediaServerItem.setRtspPort(rtspPort); | |
99 | + mediaServerItem.setRtspSslPort(rtspSslPort); | |
100 | + mediaServerItem.setAutoConfig(autoConfig); | |
101 | + mediaServerItem.setSecret(secret); | |
102 | + mediaServerItem.setRtpEnable(rtpEnable); | |
103 | + mediaServerItem.setRtpPortRange(rtpPortRange); | |
104 | + mediaServerItem.setSendRtpPortRange(rtpSendPortRange); | |
105 | + mediaServerItem.setRecordAssistPort(recordAssistPort); | |
106 | + mediaServerItem.setHookAliveInterval(30.00f); | |
107 | + mediaServerItem.setStatus(status); | |
108 | + return mediaServerItem; | |
109 | + } | |
110 | + | |
111 | + public String getHookIp() { | |
112 | + if (ObjectUtils.isEmpty(hookIp)) { | |
113 | + return sipIp.split(",")[0]; | |
114 | + } else { | |
115 | + return hookIp; | |
116 | + } | |
117 | + } | |
118 | + | |
119 | + public String getSipIp() { | |
120 | + if (sipIp == null) { | |
121 | + return this.ip; | |
122 | + } else { | |
123 | + return sipIp; | |
124 | + } | |
125 | + } | |
126 | + | |
127 | + public int getRtpProxyPort() { | |
128 | + return Objects.requireNonNullElse(rtpProxyPort, 0); | |
129 | + } | |
130 | + | |
131 | + public String getSdpIp() { | |
132 | + if (ObjectUtils.isEmpty(sdpIp)) { | |
133 | + return ip; | |
134 | + } else { | |
135 | + if (isValidIPAddress(sdpIp)) { | |
136 | + return sdpIp; | |
137 | + } else { | |
138 | + // 按照域名解析 | |
139 | + String hostAddress = null; | |
140 | + try { | |
141 | + hostAddress = InetAddress.getByName(sdpIp).getHostAddress(); | |
142 | + } catch (UnknownHostException e) { | |
143 | + log.error("[获取SDP IP]: 域名解析失败"); | |
144 | + } | |
145 | + return hostAddress; | |
146 | + } | |
147 | + } | |
148 | + } | |
149 | + | |
150 | + public String getStreamIp() { | |
151 | + if (ObjectUtils.isEmpty(streamIp)) { | |
152 | + return ip; | |
153 | + } else { | |
154 | + return streamIp; | |
155 | + } | |
156 | + } | |
157 | + | |
158 | + private boolean isValidIPAddress(String ipAddress) { | |
159 | + if ((ipAddress != null) && (!ipAddress.isEmpty())) { | |
160 | + return Pattern.matches( | |
161 | + "^([1-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])(\\.(\\d|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])){3}$", | |
162 | + ipAddress); | |
163 | + } | |
164 | + return false; | |
165 | + } | |
166 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/config/media/SipConfig.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.config.media; | |
2 | + | |
3 | +import lombok.Data; | |
4 | +import org.springframework.boot.context.properties.ConfigurationProperties; | |
5 | +import org.springframework.stereotype.Component; | |
6 | + | |
7 | +@ConfigurationProperties(prefix = "sip") | |
8 | +@Component | |
9 | +@Data | |
10 | +public class SipConfig { | |
11 | + private String ip; | |
12 | + private String showIp; | |
13 | + private Integer port; | |
14 | + private String id; | |
15 | + private String domain; | |
16 | + private String password; | |
17 | + Integer ptzSpeed = 50; | |
18 | + Integer registerTimeInterval = 120; | |
19 | + private boolean alarm; | |
20 | + | |
21 | + public String getShowIp() { | |
22 | + if (this.showIp == null) { | |
23 | + return this.ip; | |
24 | + } | |
25 | + return showIp; | |
26 | + } | |
27 | +} | ... | ... |
1 | +package org.thingsboard.server.common.data.yunteng.config.media; | |
2 | + | |
3 | +import lombok.Data; | |
4 | +import org.springframework.beans.factory.annotation.Value; | |
5 | +import org.springframework.stereotype.Component; | |
6 | + | |
7 | +@Component | |
8 | +@Data | |
9 | +public class ThingsKitVersionConfig { | |
10 | + @Value("${thingskit.release.version}") | |
11 | + private String releaseVersion; | |
12 | + | |
13 | + @Value("${thingskit.release.date}") | |
14 | + private String releaseDate; | |
15 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/config/media/UserSetting.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.config.media; | |
2 | + | |
3 | +import lombok.Data; | |
4 | +import org.springframework.boot.context.properties.ConfigurationProperties; | |
5 | +import org.springframework.stereotype.Component; | |
6 | + | |
7 | +@ConfigurationProperties(prefix = "user-settings") | |
8 | +@Component | |
9 | +@Data | |
10 | +public class UserSetting { | |
11 | + private String serverId = "000000"; | |
12 | + private Boolean seniorSdp = Boolean.FALSE; | |
13 | + private Boolean pushAuthority = Boolean.TRUE; | |
14 | + private Boolean recordSip = Boolean.TRUE; | |
15 | + private Boolean recordPushLive = Boolean.TRUE; | |
16 | + private String recordPath = null; | |
17 | + private Boolean streamOnDemand = Boolean.TRUE; | |
18 | + private Boolean sipUseSourceIpAsRemoteAddress = Boolean.FALSE; | |
19 | + private Integer playTimeout = 30000; | |
20 | +} | ... | ... |
1 | +package org.thingsboard.server.common.data.yunteng.config.media; | |
2 | + | |
3 | +import com.fasterxml.jackson.annotation.JsonProperty; | |
4 | +import lombok.Data; | |
5 | + | |
6 | +/** ZLMediaKit流媒体配置文件,具体配置说明可以参考ZLMediaKit的config.ini文件 */ | |
7 | +@Data | |
8 | +public class ZLMediaKitServerConfig { | |
9 | + @JsonProperty(value = "api.apiDebug") | |
10 | + private String apiDebug; | |
11 | + | |
12 | + @JsonProperty(value = "api.secret") | |
13 | + private String apiSecret; | |
14 | + | |
15 | + @JsonProperty(value = "api.snapRoot") | |
16 | + private String apiSnapRoot; | |
17 | + | |
18 | + @JsonProperty(value = "api.defaultSnap") | |
19 | + private String apiDefaultSnap; | |
20 | + | |
21 | + @JsonProperty(value = "ffmpeg.bin") | |
22 | + private String ffmpegBin; | |
23 | + | |
24 | + @JsonProperty(value = "ffmpeg.cmd") | |
25 | + private String ffmpegCmd; | |
26 | + | |
27 | + @JsonProperty(value = "ffmpeg.snap") | |
28 | + private String ffmpegSnap; | |
29 | + | |
30 | + @JsonProperty(value = "ffmpeg.log") | |
31 | + private String ffmpegLog; | |
32 | + | |
33 | + @JsonProperty(value = "ffmpeg.restart_sec") | |
34 | + private String ffmpegRestartSec; | |
35 | + | |
36 | + @JsonProperty(value = "protocol.modify_stamp") | |
37 | + private String protocolModifyStamp; | |
38 | + | |
39 | + @JsonProperty(value = "protocol.enable_audio") | |
40 | + private String protocolEnableAudio; | |
41 | + | |
42 | + @JsonProperty(value = "protocol.add_mute_audio") | |
43 | + private String protocolAddMuteAudio; | |
44 | + | |
45 | + @JsonProperty(value = "protocol.continue_push_ms") | |
46 | + private String protocolContinuePushMs; | |
47 | + | |
48 | + @JsonProperty(value = "protocol.enable_hls") | |
49 | + private String protocolEnableHls; | |
50 | + | |
51 | + @JsonProperty(value = "protocol.enable_mp4") | |
52 | + private String protocolEnableMp4; | |
53 | + | |
54 | + @JsonProperty(value = "protocol.enable_rtsp") | |
55 | + private String protocolEnableRtsp; | |
56 | + | |
57 | + @JsonProperty(value = "protocol.enable_rtmp") | |
58 | + private String protocolEnableRtmp; | |
59 | + | |
60 | + @JsonProperty(value = "protocol.enable_ts") | |
61 | + private String protocolEnableTs; | |
62 | + | |
63 | + @JsonProperty(value = "protocol.enable_fmp4") | |
64 | + private String protocolEnableFmp4; | |
65 | + | |
66 | + @JsonProperty(value = "protocol.mp4_as_player") | |
67 | + private String protocolMp4AsPlayer; | |
68 | + | |
69 | + @JsonProperty(value = "protocol.mp4_max_second") | |
70 | + private String protocolMp4MaxSecond; | |
71 | + | |
72 | + @JsonProperty(value = "protocol.mp4_save_path") | |
73 | + private String protocolMp4SavePath; | |
74 | + | |
75 | + @JsonProperty(value = "protocol.hls_save_path") | |
76 | + private String protocolHlsSavePath; | |
77 | + | |
78 | + @JsonProperty(value = "protocol.hls_demand") | |
79 | + private String protocolHlsDemand; | |
80 | + | |
81 | + @JsonProperty(value = "protocol.rtsp_demand") | |
82 | + private String protocolRtspDemand; | |
83 | + | |
84 | + @JsonProperty(value = "protocol.rtmp_demand") | |
85 | + private String protocolRtmpDemand; | |
86 | + | |
87 | + @JsonProperty(value = "protocol.ts_demand") | |
88 | + private String protocolTsDemand; | |
89 | + | |
90 | + @JsonProperty(value = "protocol.fmp4_demand") | |
91 | + private String protocolFmp4Demand; | |
92 | + | |
93 | + @JsonProperty(value = "general.enableVhost") | |
94 | + private String generalEnableVhost; | |
95 | + | |
96 | + @JsonProperty(value = "general.flowThreshold") | |
97 | + private String generalFlowThreshold; | |
98 | + | |
99 | + @JsonProperty(value = "general.maxStreamWaitMS") | |
100 | + private String generalMaxStreamWaitMS; | |
101 | + | |
102 | + @JsonProperty(value = "general.streamNoneReaderDelayMS") | |
103 | + private int generalStreamNoneReaderDelayMS; | |
104 | + | |
105 | + @JsonProperty(value = "general.resetWhenRePlay") | |
106 | + private String generalResetWhenRePlay; | |
107 | + | |
108 | + @JsonProperty(value = "general.mergeWriteMS") | |
109 | + private String generalMergeWriteMS; | |
110 | + | |
111 | + @JsonProperty(value = "general.mediaServerId") | |
112 | + private String generalMediaServerId; | |
113 | + | |
114 | + @JsonProperty(value = "general.wait_track_ready_ms") | |
115 | + private String generalWaitTrackReadyMs; | |
116 | + | |
117 | + @JsonProperty(value = "general.wait_add_track_ms") | |
118 | + private String generalWaitAddTrackMs; | |
119 | + | |
120 | + @JsonProperty(value = "general.unready_frame_cache") | |
121 | + private String generalUnreadyFrameCache; | |
122 | + | |
123 | + @JsonProperty(value = "hls.fileBufSize") | |
124 | + private String hlsFileBufSize; | |
125 | + | |
126 | + @JsonProperty(value = "hls.filePath") | |
127 | + private String hlsFilePath; | |
128 | + | |
129 | + @JsonProperty(value = "hls.segDur") | |
130 | + private String hlsSegDur; | |
131 | + | |
132 | + @JsonProperty(value = "hls.segNum") | |
133 | + private String hlsSegNum; | |
134 | + | |
135 | + @JsonProperty(value = "hls.segRetain") | |
136 | + private String hlsSegRetain; | |
137 | + | |
138 | + @JsonProperty(value = "hls.broadcastRecordTs") | |
139 | + private String hlsBroadcastRecordTs; | |
140 | + | |
141 | + @JsonProperty(value = "hls.deleteDelaySec") | |
142 | + private String hlsDeleteDelaySec; | |
143 | + | |
144 | + @JsonProperty(value = "hls.segKeep") | |
145 | + private String hlsSegKeep; | |
146 | + | |
147 | + @JsonProperty(value = "hook.access_file_except_hls") | |
148 | + private String hookAccessFileExceptHLS; | |
149 | + | |
150 | + @JsonProperty(value = "hook.admin_params") | |
151 | + private String hookAdminParams; | |
152 | + | |
153 | + @JsonProperty(value = "hook.alive_interval") | |
154 | + private Float hookAliveInterval; | |
155 | + | |
156 | + @JsonProperty(value = "hook.enable") | |
157 | + private String hookEnable; | |
158 | + | |
159 | + @JsonProperty(value = "hook.on_flow_report") | |
160 | + private String hookOnFlowReport; | |
161 | + | |
162 | + @JsonProperty(value = "hook.on_http_access") | |
163 | + private String hookOnHttpAccess; | |
164 | + | |
165 | + @JsonProperty(value = "hook.on_play") | |
166 | + private String hookOnPlay; | |
167 | + | |
168 | + @JsonProperty(value = "hook.on_publish") | |
169 | + private String hookOnPublish; | |
170 | + | |
171 | + @JsonProperty(value = "hook.on_record_mp4") | |
172 | + private String hookOnRecordMp4; | |
173 | + | |
174 | + @JsonProperty(value = "hook.on_rtsp_auth") | |
175 | + private String hookOnRtspAuth; | |
176 | + | |
177 | + @JsonProperty(value = "hook.on_rtsp_realm") | |
178 | + private String hookOnRtspRealm; | |
179 | + | |
180 | + @JsonProperty(value = "hook.on_shell_login") | |
181 | + private String hookOnShellLogin; | |
182 | + | |
183 | + @JsonProperty(value = "hook.on_stream_changed") | |
184 | + private String hookOnStreamChanged; | |
185 | + | |
186 | + @JsonProperty(value = "hook.on_stream_none_reader") | |
187 | + private String hookOnStreamNoneReader; | |
188 | + | |
189 | + @JsonProperty(value = "hook.on_stream_not_found") | |
190 | + private String hookOnStreamNotFound; | |
191 | + | |
192 | + @JsonProperty(value = "hook.on_server_started") | |
193 | + private String hookOnServerStarted; | |
194 | + | |
195 | + @JsonProperty(value = "hook.on_server_keepalive") | |
196 | + private String hookOnServerKeepalive; | |
197 | + | |
198 | + @JsonProperty(value = "hook.on_send_rtp_stopped") | |
199 | + private String hookOnSendRtpStopped; | |
200 | + | |
201 | + @JsonProperty(value = "hook.on_rtp_server_timeout") | |
202 | + private String hookOnRtpServerTimeout; | |
203 | + | |
204 | + @JsonProperty(value = "hook.timeoutSec") | |
205 | + private String hookTimeoutSec; | |
206 | + | |
207 | + @JsonProperty(value = "http.charSet") | |
208 | + private String httpCharSet; | |
209 | + | |
210 | + @JsonProperty(value = "http.keepAliveSecond") | |
211 | + private String httpKeepAliveSecond; | |
212 | + | |
213 | + @JsonProperty(value = "http.maxReqCount") | |
214 | + private String httpMaxReqCount; | |
215 | + | |
216 | + @JsonProperty(value = "http.maxReqSize") | |
217 | + private String httpMaxReqSize; | |
218 | + | |
219 | + @JsonProperty(value = "http.notFound") | |
220 | + private String httpNotFound; | |
221 | + | |
222 | + @JsonProperty(value = "http.port") | |
223 | + private int httpPort; | |
224 | + | |
225 | + @JsonProperty(value = "http.rootPath") | |
226 | + private String httpRootPath; | |
227 | + | |
228 | + @JsonProperty(value = "http.sendBufSize") | |
229 | + private String httpSendBufSize; | |
230 | + | |
231 | + @JsonProperty(value = "http.sslport") | |
232 | + private int httpSslPort; | |
233 | + | |
234 | + @JsonProperty(value = "multicast.addrMax") | |
235 | + private String multicastAddrMax; | |
236 | + | |
237 | + @JsonProperty(value = "multicast.addrMin") | |
238 | + private String multicastAddrMin; | |
239 | + | |
240 | + @JsonProperty(value = "multicast.udpTTL") | |
241 | + private String multicastUdpTTL; | |
242 | + | |
243 | + @JsonProperty(value = "record.appName") | |
244 | + private String recordAppName; | |
245 | + | |
246 | + @JsonProperty(value = "record.filePath") | |
247 | + private String recordFilePath; | |
248 | + | |
249 | + @JsonProperty(value = "record.fileSecond") | |
250 | + private String recordFileSecond; | |
251 | + | |
252 | + @JsonProperty(value = "record.sampleMS") | |
253 | + private String recordFileSampleMS; | |
254 | + | |
255 | + @JsonProperty(value = "rtmp.handshakeSecond") | |
256 | + private String rtmpHandshakeSecond; | |
257 | + | |
258 | + @JsonProperty(value = "rtmp.keepAliveSecond") | |
259 | + private String rtmpKeepAliveSecond; | |
260 | + | |
261 | + @JsonProperty(value = "rtmp.modifyStamp") | |
262 | + private String rtmpModifyStamp; | |
263 | + | |
264 | + @JsonProperty(value = "rtmp.port") | |
265 | + private int rtmpPort; | |
266 | + | |
267 | + @JsonProperty(value = "rtmp.sslport") | |
268 | + private int rtmpSslPort; | |
269 | + | |
270 | + @JsonProperty(value = "rtp.audioMtuSize") | |
271 | + private String rtpAudioMtuSize; | |
272 | + | |
273 | + @JsonProperty(value = "rtp.clearCount") | |
274 | + private String rtpClearCount; | |
275 | + | |
276 | + @JsonProperty(value = "rtp.cycleMS") | |
277 | + private String rtpCycleMS; | |
278 | + | |
279 | + @JsonProperty(value = "rtp.maxRtpCount") | |
280 | + private String rtpMaxRtpCount; | |
281 | + | |
282 | + @JsonProperty(value = "rtp.videoMtuSize") | |
283 | + private String rtpVideoMtuSize; | |
284 | + | |
285 | + @JsonProperty(value = "rtp_proxy.checkSource") | |
286 | + private String rtpProxyCheckSource; | |
287 | + | |
288 | + @JsonProperty(value = "rtp_proxy.dumpDir") | |
289 | + private String rtpProxyDumpDir; | |
290 | + | |
291 | + @JsonProperty(value = "rtp_proxy.port") | |
292 | + private int rtpProxyPort; | |
293 | + | |
294 | + @JsonProperty(value = "rtp_proxy.port_range") | |
295 | + private String portRange; | |
296 | + | |
297 | + @JsonProperty(value = "rtp_proxy.timeoutSec") | |
298 | + private String rtpProxyTimeoutSec; | |
299 | + | |
300 | + @JsonProperty(value = "rtsp.authBasic") | |
301 | + private String rtspAuthBasic; | |
302 | + | |
303 | + @JsonProperty(value = "rtsp.handshakeSecond") | |
304 | + private String rtspHandshakeSecond; | |
305 | + | |
306 | + @JsonProperty(value = "rtsp.keepAliveSecond") | |
307 | + private String rtspKeepAliveSecond; | |
308 | + | |
309 | + @JsonProperty(value = "rtsp.port") | |
310 | + private int rtspPort; | |
311 | + | |
312 | + @JsonProperty(value = "rtsp.sslport") | |
313 | + private int rtspSslPort; | |
314 | + | |
315 | + @JsonProperty(value = "shell.maxReqSize") | |
316 | + private String shellMaxReqSize; | |
317 | + | |
318 | + @JsonProperty(value = "shell.port") | |
319 | + private String shellPort; | |
320 | + | |
321 | + @JsonProperty(value = "ip") | |
322 | + private String ip; | |
323 | + | |
324 | + private String sdpIp; | |
325 | + | |
326 | + private String streamIp; | |
327 | + | |
328 | + private String hookIp; | |
329 | + private String tenantId; | |
330 | +} | ... | ... |
... | ... | @@ -37,6 +37,175 @@ public interface FastIotConstants { |
37 | 37 | String ORGANIZATION = "organization"; |
38 | 38 | String SCENE_REACT = "sceneReact"; |
39 | 39 | } |
40 | + interface ModBusRTU{ | |
41 | + /** | |
42 | + * 原始数据类型 | |
43 | + */ | |
44 | + String ORIGINAL_DATA_TYPE = "originalDataType"; | |
45 | + /** | |
46 | + * 寄存器地址 | |
47 | + */ | |
48 | + String REGISTER_ADDRESS = "registerAddress"; | |
49 | + | |
50 | + /** | |
51 | + * 操作功能码类型 | |
52 | + */ | |
53 | + String OPERATION_TYPE = "operationType"; | |
54 | + /** | |
55 | + * 缩放因子 | |
56 | + */ | |
57 | + String SCALE_FACTOR = "scaling"; | |
58 | + | |
59 | + /** | |
60 | + * 原始hex的标识符 | |
61 | + */ | |
62 | + String SOURCE_DATA_IDENTIFY = "source"; | |
63 | + | |
64 | + /** | |
65 | + * 比特位置 | |
66 | + */ | |
67 | + String BIT_MASK = "bitMask"; | |
68 | + } | |
69 | + interface CacheSipKey { | |
70 | + String TK_SIP_CACHE_NAME = "tkSipCacheName"; | |
71 | + String TK_OTHER_RECEIVE_RTP_INFO = "TK_OTHER_RECEIVE_RTP_INFO_"; | |
72 | + | |
73 | + String TK_OTHER_RECEIVE_PS_INFO = "TK_OTHER_RECEIVE_PS_INFO_"; | |
74 | + } | |
75 | + | |
76 | + interface MediaServerKey { | |
77 | + | |
78 | + String MEDIA_SERVER_CACHE_NAME = "tkMediaServerCacheName"; | |
79 | + String MEDIA_SERVER_PREFIX = "TK_MEDIA_SERVER_"; | |
80 | + String MEDIA_TRANSACTION_USED_PREFIX = "TK_MEDIA_TRANSACTION_"; | |
81 | + String MEDIA_SERVERS_ONLINE_PREFIX = "TK_MEDIA_ONLINE_SERVERS_"; | |
82 | + String MEDIA_STREAM_AUTHORITY = "TK_MEDIA_STREAM_AUTHORITY_"; | |
83 | + | |
84 | + /** 点播信息的缓存键 */ | |
85 | + String PLAYER_PREFIX = "TK_PLAYER"; | |
86 | + | |
87 | + /** 回放信息的缓存键 */ | |
88 | + String PLAY_BACK_PREFIX = "TK_PLAY_BACK"; | |
89 | + | |
90 | + String TK_SERVER_STREAM_PREFIX = "TK_SIGNALLING_STREAM_"; | |
91 | + | |
92 | + String PLATFORM_SEND_RTP_INFO_PREFIX = "PLATFORM_SEND_RTP_INFO_"; | |
93 | + } | |
94 | + | |
95 | + interface ZLMediaKitHttpApi { | |
96 | + String BASE_URL = "/index/api"; | |
97 | + | |
98 | + /** 获取流列表,可选筛选参数 */ | |
99 | + String GET_MEDIA_LIST = BASE_URL + "/getMediaList"; | |
100 | + | |
101 | + /** 获取rtp代理时的某路ssrc rtp信息 */ | |
102 | + String GET_RTP_INFO = BASE_URL + "/getRtpInfo"; | |
103 | + | |
104 | + /** 获取视频流截图 */ | |
105 | + String GET_STREAM_SNAP = BASE_URL + "/getSnap"; | |
106 | + | |
107 | + /** 通过fork FFmpeg进程的方式拉流代理,支持任意协议 */ | |
108 | + String ADD_FFMPEG_SOURCE = BASE_URL + "/addFFmpegSource"; | |
109 | + | |
110 | + /** 关闭ffmpeg拉流代理(流注册成功后,也可以使用close_streams接口替代) */ | |
111 | + String DEL_FFMPEG_SOURCE = BASE_URL + "/delFFmpegSource"; | |
112 | + | |
113 | + /** 获取服务器配置 */ | |
114 | + String GET_SERVER_CONFIG = BASE_URL + "/getServerConfig"; | |
115 | + | |
116 | + /** 设置服务器配置 */ | |
117 | + String SET_SERVER_CONFIG = BASE_URL + "/setServerConfig"; | |
118 | + | |
119 | + /** 创建GB28181 RTP接收端口,如果该端口接收数据超时,则会自动被回收(不用调用closeRtpServer接口) */ | |
120 | + String OPEN_RTP_SERVER = BASE_URL + "/openRtpServer"; | |
121 | + | |
122 | + /** 关闭GB28181 RTP接收端口 */ | |
123 | + String CLOSE_RTP_SERVER = BASE_URL + "/closeRtpServer"; | |
124 | + | |
125 | + /** | |
126 | + * 作为GB28181客户端,启动ps-rtp推流,支持rtp/udp方式;该接口支持rtsp/rtmp等协议转ps-rtp推流。 | |
127 | + * 第一次推流失败会直接返回错误,成功一次后,后续失败也将无限重试。 | |
128 | + */ | |
129 | + String START_SEND_RTP = BASE_URL + "/startSendRtp"; | |
130 | + | |
131 | + /** 停止GB28181 ps-rtp推流 */ | |
132 | + String STOP_SEND_RTP = BASE_URL + "/stopSendRtp"; | |
133 | + | |
134 | + /** 重启服务器,只有Daemon方式才能重启,否则是直接关闭! */ | |
135 | + String RESTART_SERVER = BASE_URL + "/restartServer"; | |
136 | + } | |
137 | + | |
138 | + interface ZLMediaBody { | |
139 | + /** 表单数据:异常码 */ | |
140 | + String CODE = "code"; | |
141 | + | |
142 | + /** 表单数据:数据 */ | |
143 | + String DATA = "data"; | |
144 | + | |
145 | + /** 表单数据:异常消息 */ | |
146 | + String MSG = "msg"; | |
147 | + | |
148 | + /** 表单数据:流媒体密钥 */ | |
149 | + String SECRET = "secret"; | |
150 | + | |
151 | + /** 流媒体服务器信息 */ | |
152 | + String MEDIA = "media"; | |
153 | + | |
154 | + /** 流媒体服务器信息:ID */ | |
155 | + String MEDIA_ID = "mediaId"; | |
156 | + | |
157 | + /** 流媒体服务器信息:会话描述协议 */ | |
158 | + String MEDIA_SDP_IP = "sdpIp"; | |
159 | + | |
160 | + /** 数据流服务ID */ | |
161 | + String SSRCINFO_STREAM = "streamId"; | |
162 | + | |
163 | + /** 数据流服务的端口 */ | |
164 | + String SSRCINFO_PORT = "streamPort"; | |
165 | + | |
166 | + /** 同步源标识符 */ | |
167 | + String SSRCINFO_SSRC = "ssrc"; | |
168 | + | |
169 | + /** 流媒体通道ID */ | |
170 | + String CHANNEL_ID = "channelId"; | |
171 | + | |
172 | + /** 流媒体通道ID */ | |
173 | + String CALL_ID = "callId"; | |
174 | + | |
175 | + /** 设备国标编号 */ | |
176 | + String CAMERA_CODE = "cameraCode"; | |
177 | + | |
178 | + /** 方法类型 */ | |
179 | + String METHOD_TYPE = "methodType"; | |
180 | + | |
181 | + /** 请求响应事件:头部标签内容 */ | |
182 | + String MSG_HEADER = "msgHeader"; | |
183 | + | |
184 | + /** 请求响应事件:表单内容 */ | |
185 | + String MSG_CONTEXT = "msgContext"; | |
186 | + | |
187 | + /** 消息类型,例如:查询、控制等 */ | |
188 | + String MSG_TYPE = "xmlRoot"; | |
189 | + | |
190 | + /** 订阅处理结果 */ | |
191 | + String SESSION_TYPE = "sessionType"; | |
192 | + | |
193 | + /** 订阅处理结果 */ | |
194 | + String SSRC_CHECK = "ssrcCheck"; | |
195 | + } | |
196 | + | |
197 | + /** 视频流传输模式 */ | |
198 | + interface StreamMode { | |
199 | + /** tcp 被动模式 */ | |
200 | + String TCP_PASSIVE = "TCP-PASSIVE"; | |
201 | + | |
202 | + /** tcp 主动模式 */ | |
203 | + String TCP_ACTIVE = "TCP-ACTIVE"; | |
204 | + | |
205 | + /** udp */ | |
206 | + String UDP = "UDP"; | |
207 | + } | |
208 | + | |
40 | 209 | |
41 | 210 | interface TBCacheConfig { |
42 | 211 | String TB_CACHE_CONFIG_KEY = "TB_CONNECT_CACHE"; |
... | ... | @@ -66,6 +235,23 @@ public interface FastIotConstants { |
66 | 235 | String NODE = "NODE"; |
67 | 236 | } |
68 | 237 | |
238 | + /** | |
239 | + * 首页统计分析指标 | |
240 | + */ | |
241 | + interface Statistics { | |
242 | + String TRANSPORT_MSG_COUNT_HOURLY = "transportMsgCountHourly"; | |
243 | + String TRANSPORT_DATAPOINTS_COUNT_HOURLY = "transportDataPointsCountHourly"; | |
244 | + String CREATED_ALARMS_COUNT_HOURLY = "createdAlarmsCountHourly"; | |
245 | + String TRANSPORT_MSG_COUNT = "transportMsgCount"; | |
246 | + String TRANSPORT_DATAPOINTS_COUNT = "transportDataPointsCount"; | |
247 | + String CREATED_ALARMS_COUNT = "createdAlarmsCount"; | |
248 | + } | |
249 | + /** | |
250 | + * 单位转换常量 | |
251 | + */ | |
252 | + interface Unit { | |
253 | + Long ONE_DAY_MILLISECONDS = 86400000L; | |
254 | + } | |
69 | 255 | class DefaultOrder { |
70 | 256 | public static final String CREATE_TIME = "create_time"; |
71 | 257 | } |
... | ... | @@ -119,6 +305,7 @@ public interface FastIotConstants { |
119 | 305 | class MagicNumber { |
120 | 306 | public static final int ZERO = 0; |
121 | 307 | public static final int ONE = 1; |
308 | + public static final int TWO = 2; | |
122 | 309 | public static final int TEN = 10; |
123 | 310 | } |
124 | 311 | |
... | ... | @@ -219,4 +406,22 @@ public interface FastIotConstants { |
219 | 406 | public static String TK_MSG_EVENT_NODE = "org.thingsboard.rule.engine.yunteng.event.TkMsgEventNode"; |
220 | 407 | } |
221 | 408 | |
409 | + | |
410 | + /** 设备扩展信息内容 */ | |
411 | + interface DeviceAdditional { | |
412 | + /** 摄像头信息 */ | |
413 | + String SIP = "sip"; | |
414 | + | |
415 | + /** 设备图片 */ | |
416 | + String AVATAR = "avatar"; | |
417 | + | |
418 | + /** 设备地理位置 */ | |
419 | + String ADDRESS = "address"; | |
420 | + | |
421 | + /** 地理坐标之经度 */ | |
422 | + String LONGITUDE = "longitude"; | |
423 | + | |
424 | + /** 地理坐标之维度 */ | |
425 | + String LATITUDE = "latitude"; | |
426 | + } | |
222 | 427 | } | ... | ... |
... | ... | @@ -133,6 +133,15 @@ public final class ModelConstants { |
133 | 133 | |
134 | 134 | /** 产品品类表 */ |
135 | 135 | public static final String TK_DEVICE_PROFILE_CATEGORY = "tk_device_profile_category"; |
136 | + | |
137 | + /** 设备接入信息表 */ | |
138 | + public static final String TK_DEVICE_ACCESS_INFORMATION = "tk_device_access_information"; | |
139 | + | |
140 | + /** ZLMediaKit 流媒体表 */ | |
141 | + public static final String TK_MEDIA_SERVER_NAME = "tk_media_server"; | |
142 | + | |
143 | + /** 视频通道表 */ | |
144 | + public static final String TK_VIDEO_CHANNEL_NAME = "tk_video_channel"; | |
136 | 145 | } |
137 | 146 | |
138 | 147 | public static class TableFields { | ... | ... |
1 | +package org.thingsboard.server.common.data.yunteng.constant; | |
2 | + | |
3 | +public class SipRequestMethodConstants { | |
4 | + public static final String ACK = "ACK"; | |
5 | + public static final String BYE = "BYE"; | |
6 | + public static final String CANCEL = "CANCEL"; | |
7 | + public static final String INVITE = "INVITE"; | |
8 | + public static final String OPTIONS = "OPTIONS"; | |
9 | + public static final String REGISTER = "REGISTER"; | |
10 | + public static final String NOTIFY = "NOTIFY"; | |
11 | + public static final String SUBSCRIBE = "SUBSCRIBE"; | |
12 | + public static final String MESSAGE = "MESSAGE"; | |
13 | + public static final String REFER = "REFER"; | |
14 | + | |
15 | + public static final String INFO = "INFO"; | |
16 | + public static final String PRACK = "PRACK"; | |
17 | + | |
18 | + public static final String UPDATE = "UPDATE"; | |
19 | + | |
20 | + public static final String PUBLISH = "PUBLISH"; | |
21 | +} | ... | ... |
... | ... | @@ -120,6 +120,22 @@ public enum ErrorMessage { |
120 | 120 | EZVIZ_API_ERROR(400095,"荧石视频获取TokenAPI调用失败【%s】,错误码【%s】"), |
121 | 121 | EZVIZ_GET_URL_ERROR(400096,"荧石API调用获取URL失败!!"), |
122 | 122 | IMPORT_TCP_ERROR(400097,"TCP产品不能导入INT,DOUBLE,BOOL,TEXT以外的数据类型属性!!"), |
123 | + SSRC_INFO_NOT_FOUND(400098,"缓存事务信息未找到,设备【%s】 通道【%s】"), | |
124 | + VIDEO_CHANNEL_NOT_FOUND(400099,"视频通道不存在"), | |
125 | + ONLINE_MEDIA_SERVER_NOT_FOUND(400100,"没有可用的流媒体节点"), | |
126 | + FOUND_VIDEO_DEVICE_FAILED(400101,"视频设备SIP信息丢失"), | |
127 | + NOT_FOUND_MEDIA_SERVER_FOR_PLAY(400102,"未找到可用于播放的流媒体"), | |
128 | + RECEIVE_STREAM_FAILED(400103,"开启收流失败"), | |
129 | + GET_PLAY_PORT_FAILED(400104,"从流媒体【%s】获取点播端口异常"), | |
130 | + STREAM_INFO_NOT_FOUND_FOR_PLAY(400105,"点播信息未找到"), | |
131 | + SIP_COMMAND_SEND_FAILED(400106,"sip命令下发失败"), | |
132 | + NOT_BELONG_CURRENT_CUSTOMER(400107,"该数据不属于当前客户"), | |
133 | + IMPORT_ERROR(400108,"请使用模板excel重新导入"), | |
134 | + INTRANET_ERROR(400109,"内网ip+端口不能重复,请重新输入"), | |
135 | + OUTER_NET_ERROR(400110,"外网ip+端口不能重复,请重新输入"), | |
136 | + | |
137 | + DEVICE_NOT_ONLINE(400111,"设备不在线,无法执行相关操作!"), | |
138 | + GBT_VIDEO_REPETITION(400112,"该设备通道号视频已存在!"), | |
123 | 139 | HAVE_NO_PERMISSION(500002,"没有修改权限"), |
124 | 140 | NOT_ALLOED_ISOLATED_IN_MONOLITH(500003,"【monolith】模式下,不能选择【isolated】类型的租户配置"); |
125 | 141 | ... | ... |
... | ... | @@ -128,4 +128,10 @@ public class DeviceDTO extends TenantDTO { |
128 | 128 | |
129 | 129 | @ApiModelProperty(value = "是否收藏:0否1是") |
130 | 130 | private Integer isCollect; |
131 | + | |
132 | + @ApiModelProperty(value = "OTA升级固件ID") | |
133 | + private String firmwareId; | |
134 | + | |
135 | + @ApiModelProperty(value = "OTA升级软件ID") | |
136 | + private String softwareId; | |
131 | 137 | } | ... | ... |
... | ... | @@ -88,6 +88,12 @@ public class DeviceProfileDTO extends BaseDTO { |
88 | 88 | @ApiModelProperty(value = "品类名称") |
89 | 89 | private String categoryName; |
90 | 90 | |
91 | + @ApiModelProperty(value = "OTA升级固件ID") | |
92 | + private String firmwareId; | |
93 | + | |
94 | + @ApiModelProperty(value = "OTA升级软件ID") | |
95 | + private String softwareId; | |
96 | + | |
91 | 97 | public DeviceProfileDTO() {} |
92 | 98 | |
93 | 99 | public DeviceProfileDTO( | ... | ... |
1 | +package org.thingsboard.server.common.data.yunteng.dto; | |
2 | + | |
3 | +import com.fasterxml.jackson.databind.JsonNode; | |
4 | +import io.swagger.annotations.ApiModelProperty; | |
5 | +import lombok.Data; | |
6 | +import lombok.EqualsAndHashCode; | |
7 | +import org.thingsboard.server.common.data.yunteng.common.AddGroup; | |
8 | +import org.thingsboard.server.common.data.yunteng.enums.TransportTypeEnum; | |
9 | + | |
10 | +import javax.validation.constraints.NotEmpty; | |
11 | + | |
12 | +@EqualsAndHashCode(callSuper = true) | |
13 | +@Data | |
14 | +public class TkDeviceAccessInformationDTO extends TenantDTO { | |
15 | + | |
16 | + @ApiModelProperty(value = "内网ip") | |
17 | + @NotEmpty( | |
18 | + message = "内网ip不能为空或空字符串", | |
19 | + groups = {AddGroup.class}) | |
20 | + private String intranetIp; | |
21 | + | |
22 | + @ApiModelProperty(value = "内网端口") | |
23 | + @NotEmpty( | |
24 | + message = "内网端口不能为空或空字符串", | |
25 | + groups = {AddGroup.class}) | |
26 | + private String intranetPort; | |
27 | + | |
28 | + @ApiModelProperty(value = "外网IP") | |
29 | + @NotEmpty( | |
30 | + message = "外网IP不能为空或空字符串", | |
31 | + groups = {AddGroup.class}) | |
32 | + private String outerNetIp; | |
33 | + | |
34 | + @ApiModelProperty(value = "外网端口") | |
35 | + private String outerNetPort; | |
36 | + | |
37 | + @ApiModelProperty(value = "设备接入协议") | |
38 | + private TransportTypeEnum deviceAgreement; | |
39 | + | |
40 | + @ApiModelProperty(value = "sip扩展信息: serverId 服务器id serverRegion 服务器域 password 密码") | |
41 | + private JsonNode sipExtend; | |
42 | + | |
43 | +} | ... | ... |
... | ... | @@ -2,6 +2,7 @@ package org.thingsboard.server.common.data.yunteng.dto; |
2 | 2 | |
3 | 3 | import io.swagger.annotations.ApiModelProperty; |
4 | 4 | import lombok.Data; |
5 | +import org.thingsboard.server.common.data.yunteng.enums.HexByteOrderEnum; | |
5 | 6 | import org.thingsboard.server.common.data.yunteng.enums.TkModBusCheckType; |
6 | 7 | import java.io.Serializable; |
7 | 8 | import java.util.List; |
... | ... | @@ -28,4 +29,7 @@ public class TkDeviceRpcDTO implements Serializable { |
28 | 29 | |
29 | 30 | @ApiModelProperty(value = "寄存器值") |
30 | 31 | private List<Integer> registerValues; |
32 | + | |
33 | + @ApiModelProperty(value = "顺序") | |
34 | + private HexByteOrderEnum hexByteOrderEnum; | |
31 | 35 | } | ... | ... |
... | ... | @@ -26,7 +26,6 @@ public class TkVideoDTO extends TenantDTO { |
26 | 26 | private String deviceType; |
27 | 27 | |
28 | 28 | @ApiModelProperty(value = "摄像头编号/监控点位编号", required = true) |
29 | - @NotEmpty(message = "摄像头编号不能为空或空字符串") | |
30 | 29 | private String sn; |
31 | 30 | |
32 | 31 | @ApiModelProperty(value = "组织ID", required = true) |
... | ... | @@ -42,7 +41,7 @@ public class TkVideoDTO extends TenantDTO { |
42 | 41 | @ApiModelProperty(value = "摄像头描述") |
43 | 42 | private String description; |
44 | 43 | |
45 | - @ApiModelProperty(value = "流获取方式:0 手动填写 1平台获取", required = true) | |
44 | + @ApiModelProperty(value = "流获取方式:0 手动填写 1平台获取 2 GBT28281", required = true) | |
46 | 45 | private Integer accessMode; |
47 | 46 | |
48 | 47 | @ApiModelProperty(value = "平台ID") | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/TkVideoGbtDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto; | |
2 | + | |
3 | +import io.swagger.annotations.ApiModelProperty; | |
4 | +import lombok.Data; | |
5 | +import lombok.EqualsAndHashCode; | |
6 | + | |
7 | +import javax.validation.constraints.NotEmpty; | |
8 | +import java.util.List; | |
9 | + | |
10 | +@Data | |
11 | +@EqualsAndHashCode(callSuper = true) | |
12 | +public class TkVideoGbtDTO extends TenantDTO { | |
13 | + | |
14 | + @ApiModelProperty(value = "组织ID", required = true) | |
15 | + @NotEmpty(message = "组织ID不能为空或空字符串") | |
16 | + private String organizationId; | |
17 | + | |
18 | + @ApiModelProperty(value = "流获取方式:0 手动填写 1平台获取 2 GBT28281", required = true) | |
19 | + private Integer accessMode; | |
20 | + | |
21 | + @ApiModelProperty(value = "多个设备id与通道号") | |
22 | + private List<TkVideoGbtDeviceDTO> gbtDeviceDTOS; | |
23 | + | |
24 | + @ApiModelProperty(value = "是否全部设备 0否 1是") | |
25 | + private Integer allDevice; | |
26 | + | |
27 | +} | |
\ No newline at end of file | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/TkVideoGbtDeviceDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto; | |
2 | + | |
3 | +import io.swagger.annotations.ApiModelProperty; | |
4 | +import lombok.Data; | |
5 | + | |
6 | +import javax.validation.constraints.NotEmpty; | |
7 | +import java.util.List; | |
8 | + | |
9 | +@Data | |
10 | +public class TkVideoGbtDeviceDTO { | |
11 | + | |
12 | + @ApiModelProperty(value = "设备id", required = true) | |
13 | + @NotEmpty(message = "设备id不能为空") | |
14 | + private String deviceID; | |
15 | + | |
16 | + @ApiModelProperty(value = "设备名称", required = true) | |
17 | + @NotEmpty(message = "设备名称不能为空") | |
18 | + private String deviceName; | |
19 | + | |
20 | + @ApiModelProperty(value = "通道号集合") | |
21 | + @NotEmpty(message = "通道号集合不能为空") | |
22 | + private List<String> channelNos; | |
23 | + | |
24 | +} | |
\ No newline at end of file | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/sip/CatalogDataDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | |
2 | + | |
3 | +import lombok.Data; | |
4 | +import org.thingsboard.server.common.data.yunteng.enums.CatalogDataStatusEnum; | |
5 | + | |
6 | +import java.time.LocalDateTime; | |
7 | +import java.util.List; | |
8 | + | |
9 | +@Data | |
10 | +public class CatalogDataDTO { | |
11 | + /** 命令序列号 */ | |
12 | + private int sn; | |
13 | + | |
14 | + private int total; | |
15 | + private List<VideoChanelDTO> channelList; | |
16 | + private LocalDateTime lastTime; | |
17 | + private SipDeviceDTO device; | |
18 | + private String errorMsg; | |
19 | + private CatalogDataStatusEnum status; | |
20 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/sip/MediaServerDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | |
2 | + | |
3 | +import io.swagger.annotations.ApiModelProperty; | |
4 | +import lombok.Data; | |
5 | +import lombok.EqualsAndHashCode; | |
6 | +import org.springframework.util.ObjectUtils; | |
7 | +import org.thingsboard.server.common.data.yunteng.config.media.ZLMediaKitServerConfig; | |
8 | +import org.thingsboard.server.common.data.yunteng.dto.TenantDTO; | |
9 | + | |
10 | +@EqualsAndHashCode(callSuper = true) | |
11 | +@Data | |
12 | +public class MediaServerDTO extends TenantDTO { | |
13 | + private static final long serialVersionUID = -2499846994830954839L; | |
14 | + | |
15 | + @ApiModelProperty(value = "流媒体的ID,非主键ID") | |
16 | + private String mediaServerId; | |
17 | + | |
18 | + @ApiModelProperty(value = "流媒体的IP") | |
19 | + private String ip; | |
20 | + | |
21 | + @ApiModelProperty(value = "hook使用的IP") | |
22 | + private String hookIp; | |
23 | + | |
24 | + @ApiModelProperty(value = "SDP IP") | |
25 | + private String sdpIp; | |
26 | + | |
27 | + @ApiModelProperty(value = "流IP") | |
28 | + private String streamIp; | |
29 | + | |
30 | + @ApiModelProperty(value = "HTTP端口") | |
31 | + private Integer httpPort; | |
32 | + | |
33 | + @ApiModelProperty(value = "HTTPS端口") | |
34 | + private Integer httpSslPort; | |
35 | + | |
36 | + @ApiModelProperty(value = "RTMP端口") | |
37 | + private Integer rtmpPort; | |
38 | + | |
39 | + @ApiModelProperty(value = "RTMP安全协议端口") | |
40 | + private Integer rtmpSslPort; | |
41 | + | |
42 | + @ApiModelProperty(value = "RTP收流端口(单端口模式有用)") | |
43 | + private Integer rtpProxyPort; | |
44 | + | |
45 | + @ApiModelProperty(value = "RTSP端口") | |
46 | + private Integer rtspPort; | |
47 | + | |
48 | + @ApiModelProperty(value = "RTSP安全协议端口") | |
49 | + private Integer rtspSslPort; | |
50 | + | |
51 | + @ApiModelProperty(value = "是否开启自动配置ZLMediaKit") | |
52 | + private boolean autoConfig; | |
53 | + | |
54 | + @ApiModelProperty(value = "ZLMediaKit鉴权参数") | |
55 | + private String secret; | |
56 | + | |
57 | + @ApiModelProperty(value = "keepalive hook触发间隔,单位秒") | |
58 | + private Float hookAliveInterval; | |
59 | + | |
60 | + @ApiModelProperty(value = "是否使用多端口模式") | |
61 | + private boolean rtpEnable; | |
62 | + | |
63 | + @ApiModelProperty(value = "流媒体服务器的状态") | |
64 | + private boolean status; | |
65 | + | |
66 | + @ApiModelProperty(value = "多端口RTP收流端口范围") | |
67 | + private String rtpPortRange; | |
68 | + | |
69 | + @ApiModelProperty(value = "RTP发流端口范围") | |
70 | + private String sendRtpPortRange; | |
71 | + | |
72 | + @ApiModelProperty(value = "assist服务端口") | |
73 | + private Integer recordAssistPort; | |
74 | + | |
75 | + @ApiModelProperty(value = "最后一次心跳时间") | |
76 | + private String lastKeepaliveTime; | |
77 | + | |
78 | + @ApiModelProperty(value = "是否是默认ZLM") | |
79 | + private boolean defaultServer; | |
80 | + | |
81 | + @ApiModelProperty(value = "当前使用到的端口") | |
82 | + private Integer currentPort; | |
83 | + | |
84 | + public MediaServerDTO(){ | |
85 | + | |
86 | + } | |
87 | + public MediaServerDTO(ZLMediaKitServerConfig zlmServerConfig, String sipIp) { | |
88 | + setId(zlmServerConfig.getGeneralMediaServerId()); | |
89 | + ip = zlmServerConfig.getIp(); | |
90 | + hookIp = ObjectUtils.isEmpty(zlmServerConfig.getHookIp()) ? sipIp : zlmServerConfig.getHookIp(); | |
91 | + sdpIp = | |
92 | + ObjectUtils.isEmpty(zlmServerConfig.getSdpIp()) | |
93 | + ? zlmServerConfig.getIp() | |
94 | + : zlmServerConfig.getSdpIp(); | |
95 | + streamIp = | |
96 | + ObjectUtils.isEmpty(zlmServerConfig.getStreamIp()) | |
97 | + ? zlmServerConfig.getIp() | |
98 | + : zlmServerConfig.getStreamIp(); | |
99 | + httpPort = zlmServerConfig.getHttpPort(); | |
100 | + httpSslPort = zlmServerConfig.getHttpSslPort(); | |
101 | + rtmpPort = zlmServerConfig.getRtmpPort(); | |
102 | + rtmpSslPort = zlmServerConfig.getRtmpSslPort(); | |
103 | + rtpProxyPort = zlmServerConfig.getRtpProxyPort(); | |
104 | + rtspPort = zlmServerConfig.getRtspPort(); | |
105 | + rtspSslPort = zlmServerConfig.getRtspSslPort(); | |
106 | + autoConfig = true; // 默认值true; | |
107 | + secret = zlmServerConfig.getApiSecret(); | |
108 | + hookAliveInterval = zlmServerConfig.getHookAliveInterval(); | |
109 | + rtpEnable = false; // 默认使用单端口;直到用户自己设置开启多端口 | |
110 | + rtpPortRange = zlmServerConfig.getPortRange().replace("_", ","); // 默认使用30000,30500作为级联时发送流的端口号 | |
111 | + recordAssistPort = 0; // 默认关闭 | |
112 | + } | |
113 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/sip/PTZCmdDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | |
2 | + | |
3 | +import io.swagger.annotations.ApiModelProperty; | |
4 | +import lombok.Data; | |
5 | +import org.thingsboard.server.common.data.yunteng.enums.PTZCommandEnum; | |
6 | + | |
7 | +import java.io.Serializable; | |
8 | + | |
9 | +@Data | |
10 | +public class PTZCmdDTO implements Serializable { | |
11 | + | |
12 | + @ApiModelProperty(value = "控制指令") | |
13 | + private PTZCommandEnum command; | |
14 | + | |
15 | + @ApiModelProperty(value = "水平速度") | |
16 | + private int horizonSpeed; | |
17 | + | |
18 | + @ApiModelProperty(value = "垂直速度") | |
19 | + private int verticalSpeed; | |
20 | + | |
21 | + @ApiModelProperty(value = "缩放速度") | |
22 | + private int zoomSpeed; | |
23 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/sip/SendRtpItemDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | |
2 | + | |
3 | +import io.swagger.annotations.ApiModelProperty; | |
4 | +import lombok.Data; | |
5 | +import org.thingsboard.server.common.data.yunteng.enums.InviteStreamTypeEnum; | |
6 | + | |
7 | +@Data | |
8 | +public class SendRtpItemDTO { | |
9 | + @ApiModelProperty(value = "推流ip") | |
10 | + private String ip; | |
11 | + | |
12 | + @ApiModelProperty(value = "推流端口") | |
13 | + private int port; | |
14 | + | |
15 | + @ApiModelProperty(value = "推流标识") | |
16 | + private String ssrc; | |
17 | + | |
18 | + @ApiModelProperty(value = "平台id") | |
19 | + private String platformId; | |
20 | + | |
21 | + @ApiModelProperty(value = "对应设备id") | |
22 | + private String deviceId; | |
23 | + | |
24 | + @ApiModelProperty(value = "直播流的应用名") | |
25 | + private String app; | |
26 | + | |
27 | + @ApiModelProperty(value = "通道id") | |
28 | + private String channelId; | |
29 | + | |
30 | + @ApiModelProperty(value = "推流状态:0 等待设备推流上来 1 等待上级平台回复ack 2 推流中") | |
31 | + private int status = 0; | |
32 | + | |
33 | + @ApiModelProperty(value = "设备推流的streamId") | |
34 | + private String streamId; | |
35 | + | |
36 | + @ApiModelProperty(value = "是否为tcp") | |
37 | + private boolean tcp; | |
38 | + | |
39 | + @ApiModelProperty(value = "是否为tcp主动模式") | |
40 | + private boolean tcpActive; | |
41 | + | |
42 | + @ApiModelProperty(value = "自己推流使用的端口") | |
43 | + private int localPort; | |
44 | + | |
45 | + @ApiModelProperty(value = "使用的流媒体") | |
46 | + private String mediaServerId; | |
47 | + | |
48 | + @ApiModelProperty(value = "使用的服务的ID") | |
49 | + private String serverId; | |
50 | + | |
51 | + @ApiModelProperty(value = "invite 的 callId") | |
52 | + private String CallId; | |
53 | + | |
54 | + @ApiModelProperty(value = "invite 的 fromTag") | |
55 | + private String fromTag; | |
56 | + | |
57 | + @ApiModelProperty(value = "invite 的 toTag") | |
58 | + private String toTag; | |
59 | + | |
60 | + @ApiModelProperty(value = "发送时,rtp的pt(uint8_t),不传时默认为96") | |
61 | + private int pt = 96; | |
62 | + | |
63 | + @ApiModelProperty(value = "发送时,rtp的负载类型。为true时,负载为ps;为false时,为es") | |
64 | + private boolean usePs = true; | |
65 | + | |
66 | + @ApiModelProperty(value = "当usePs 为false时,有效。为1时,发送音频;为0时,发送视频;不传时默认为0") | |
67 | + private boolean onlyAudio = false; | |
68 | + | |
69 | + @ApiModelProperty(value = "是否开启rtcp保活") | |
70 | + private boolean rtcp = false; | |
71 | + | |
72 | + @ApiModelProperty(value = "播放类型") | |
73 | + private InviteStreamTypeEnum playType; | |
74 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/yunteng/dto/sip/SipDeviceDTO.java
0 → 100644
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | |
2 | + | |
3 | +import io.swagger.annotations.ApiModelProperty; | |
4 | +import lombok.Data; | |
5 | +import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | |
6 | + | |
7 | +import java.io.Serializable; | |
8 | + | |
9 | +@Data | |
10 | +public class SipDeviceDTO implements Serializable { | |
11 | + @ApiModelProperty(value = "设备国标编号") | |
12 | + private String cameraCode; | |
13 | + | |
14 | + @ApiModelProperty(value = "wan地址") | |
15 | + private String hostAddress; | |
16 | + | |
17 | + @ApiModelProperty(value = "设备访问平台的IP") | |
18 | + private String localIp; | |
19 | + | |
20 | + @ApiModelProperty(value = "传输协议:UDP/TCP") | |
21 | + private String transport; | |
22 | + | |
23 | + @ApiModelProperty(value = "字符集") | |
24 | + private String charset; | |
25 | + | |
26 | + @ApiModelProperty(value = "收流IP") | |
27 | + private String sdpIp; | |
28 | + | |
29 | + @ApiModelProperty(value = "数据流传输模式:UDP / TCP-ACTIVE:tcp主动模式 / TCP-PASSIVE:tcp被动模式") | |
30 | + private String streamMode; | |
31 | + | |
32 | + @ApiModelProperty(value = "通道个数") | |
33 | + private Integer channelCount; | |
34 | + | |
35 | + @ApiModelProperty(value = "设备使用的媒体id, 默认为null") | |
36 | + private String mediaServerId; | |
37 | + | |
38 | + @ApiModelProperty(value = "是否开启ssrc校验,默认关闭,开启可以防止串流") | |
39 | + private boolean ssrcCheck = true; | |
40 | + @ApiModelProperty(value = "生产厂商") | |
41 | + private String manufacturer; | |
42 | + | |
43 | + @ApiModelProperty(value = "设备名称") | |
44 | + private String name; | |
45 | + | |
46 | + @ApiModelProperty(value = "设备型号") | |
47 | + private String model; | |
48 | + | |
49 | + @ApiModelProperty(value = "固件版本") | |
50 | + private String firmware; | |
51 | + public Integer getStreamModeForParam() { | |
52 | + if (FastIotConstants.StreamMode.UDP.equalsIgnoreCase(streamMode)) { | |
53 | + return 0; | |
54 | + }else if (FastIotConstants.StreamMode.TCP_PASSIVE.equalsIgnoreCase(streamMode)) { | |
55 | + return 1; | |
56 | + }else if (FastIotConstants.StreamMode.TCP_ACTIVE.equalsIgnoreCase(streamMode)) { | |
57 | + return 2; | |
58 | + } | |
59 | + return 0; | |
60 | + } | |
61 | +} | ... | ... |
1 | +package org.thingsboard.server.common.data.yunteng.dto.sip; | |
2 | + | |
3 | +import lombok.Builder; | |
4 | +import lombok.Data; | |
5 | + | |
6 | +import java.io.Serializable; | |
7 | + | |
8 | +@Data | |
9 | +@Builder | |
10 | +public class SipMessageHeaderDTO implements Serializable { | |
11 | + private String callId; | |
12 | + private String viaTag; | |
13 | + private String fromTag; | |
14 | + private String toTag; | |
15 | + private String viaBranch; | |
16 | +} | ... | ... |