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,6 +92,10 @@ | ||
92 | </dependency> | 92 | </dependency> |
93 | <dependency> | 93 | <dependency> |
94 | <groupId>org.thingsboard.common.transport</groupId> | 94 | <groupId>org.thingsboard.common.transport</groupId> |
95 | + <artifactId>gbt28181</artifactId> | ||
96 | + </dependency> | ||
97 | + <dependency> | ||
98 | + <groupId>org.thingsboard.common.transport</groupId> | ||
95 | <artifactId>http</artifactId> | 99 | <artifactId>http</artifactId> |
96 | </dependency> | 100 | </dependency> |
97 | <dependency> | 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'); |
@@ -74,7 +74,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | @@ -74,7 +74,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt | ||
74 | 74 | ||
75 | //Thingskit function | 75 | //Thingskit function |
76 | public static final String CODE_BASED_LOGIN_ENTRY_POINT = "/api/yt/auth/code/login"; | 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 | public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public"; | 79 | public static final String PUBLIC_LOGIN_ENTRY_POINT = "/api/auth/login/public"; |
80 | public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token"; | 80 | public static final String TOKEN_REFRESH_ENTRY_POINT = "/api/auth/token"; |
@@ -82,7 +82,7 @@ public class ThingsKitExceptionHandler { | @@ -82,7 +82,7 @@ public class ThingsKitExceptionHandler { | ||
82 | SecurityUser currentUser = getCurrentUser(); | 82 | SecurityUser currentUser = getCurrentUser(); |
83 | 83 | ||
84 | Asset entity = new Asset(); | 84 | Asset entity = new Asset(); |
85 | - entity.setName(e.getMessage()); | 85 | + entity.setName(e.getClass().getName()); |
86 | 86 | ||
87 | // 请求相关信息 | 87 | // 请求相关信息 |
88 | SysLogOperateDTO additionalInfo = new SysLogOperateDTO(); | 88 | SysLogOperateDTO additionalInfo = new SysLogOperateDTO(); |
1 | package org.thingsboard.server.controller.yunteng; | 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 | import io.swagger.annotations.Api; | 6 | import io.swagger.annotations.Api; |
4 | import io.swagger.annotations.ApiOperation; | 7 | import io.swagger.annotations.ApiOperation; |
5 | import io.swagger.annotations.ApiParam; | 8 | import io.swagger.annotations.ApiParam; |
9 | +import java.util.HashMap; | ||
10 | +import java.util.List; | ||
11 | +import java.util.concurrent.ExecutionException; | ||
6 | import lombok.RequiredArgsConstructor; | 12 | import lombok.RequiredArgsConstructor; |
7 | import org.springframework.http.HttpStatus; | 13 | import org.springframework.http.HttpStatus; |
8 | import org.springframework.http.ResponseEntity; | 14 | import org.springframework.http.ResponseEntity; |
@@ -23,91 +29,87 @@ import org.thingsboard.server.dao.exception.DataValidationException; | @@ -23,91 +29,87 @@ import org.thingsboard.server.dao.exception.DataValidationException; | ||
23 | import org.thingsboard.server.dao.yunteng.service.HomePageService; | 29 | import org.thingsboard.server.dao.yunteng.service.HomePageService; |
24 | import org.thingsboard.server.service.security.model.SecurityUser; | 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 | @RequestMapping("api/yt/homepage") | 32 | @RequestMapping("api/yt/homepage") |
34 | @Api(tags = {"首页"}) | 33 | @Api(tags = {"首页"}) |
35 | @RequiredArgsConstructor | 34 | @RequiredArgsConstructor |
36 | @RestController | 35 | @RestController |
37 | public class HomePageController extends BaseController { | 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,19 +22,18 @@ import org.thingsboard.server.common.data.id.DeviceProfileId; | ||
22 | import org.thingsboard.server.common.data.yunteng.common.AddGroup; | 22 | import org.thingsboard.server.common.data.yunteng.common.AddGroup; |
23 | import org.thingsboard.server.common.data.yunteng.common.DeleteGroup; | 23 | import org.thingsboard.server.common.data.yunteng.common.DeleteGroup; |
24 | import org.thingsboard.server.common.data.yunteng.common.UpdateGroup; | 24 | import org.thingsboard.server.common.data.yunteng.common.UpdateGroup; |
25 | +import org.thingsboard.server.common.data.yunteng.core.exception.ThingsKitException; | ||
25 | import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; | 26 | import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; |
26 | import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; | 27 | import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; |
27 | import org.thingsboard.server.common.data.yunteng.dto.*; | 28 | import org.thingsboard.server.common.data.yunteng.dto.*; |
28 | import org.thingsboard.server.common.data.yunteng.dto.thingsmodel.ImportThingsModelDTO; | 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 | import org.thingsboard.server.common.data.yunteng.utils.tools.ResponseResult; | 31 | import org.thingsboard.server.common.data.yunteng.utils.tools.ResponseResult; |
34 | import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; | 32 | import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; |
35 | import org.thingsboard.server.controller.BaseController; | 33 | import org.thingsboard.server.controller.BaseController; |
36 | import org.thingsboard.server.dao.yunteng.service.ThingsModelService; | 34 | import org.thingsboard.server.dao.yunteng.service.ThingsModelService; |
37 | import org.thingsboard.server.dao.yunteng.service.TkDeviceProfileService; | 35 | import org.thingsboard.server.dao.yunteng.service.TkDeviceProfileService; |
36 | +import org.thingsboard.server.utils.ImportModbusUtils; | ||
38 | 37 | ||
39 | import java.io.File; | 38 | import java.io.File; |
40 | import java.io.IOException; | 39 | import java.io.IOException; |
@@ -42,6 +41,7 @@ import java.io.InputStream; | @@ -42,6 +41,7 @@ import java.io.InputStream; | ||
42 | import java.util.HashMap; | 41 | import java.util.HashMap; |
43 | import java.util.List; | 42 | import java.util.List; |
44 | import java.util.Map; | 43 | import java.util.Map; |
44 | +import java.util.regex.Pattern; | ||
45 | 45 | ||
46 | import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.*; | 46 | import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.*; |
47 | import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.ORDER_TYPE; | 47 | import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant.ORDER_TYPE; |
@@ -234,16 +234,221 @@ public class ThingsModelController extends BaseController { | @@ -234,16 +234,221 @@ public class ThingsModelController extends BaseController { | ||
234 | return thingsModelService.saveOrUpdate(thingsModelDTO); | 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 | @ApiOperation(value = "通过excel导入物模型") | 272 | @ApiOperation(value = "通过excel导入物模型") |
238 | @PreAuthorize("@check.checkPermissions({'SYS_ADMIN','PLATFORM_ADMIN','TENANT_ADMIN'},{'api:yt:things_model:excel_import'})") | 273 | @PreAuthorize("@check.checkPermissions({'SYS_ADMIN','PLATFORM_ADMIN','TENANT_ADMIN'},{'api:yt:things_model:excel_import'})") |
239 | @PostMapping("/csvImport") | 274 | @PostMapping("/csvImport") |
240 | - public ResponseResult<String> csvImport(String deviceProfileId, String categoryId, | 275 | + public ResponseResult<String> csvImport(String deviceProfileId, String categoryId,String type, |
241 | @RequestPart("file") MultipartFile file) throws Exception { | 276 | @RequestPart("file") MultipartFile file) throws Exception { |
242 | if(file.isEmpty()){ | 277 | if(file.isEmpty()){ |
243 | - return null; | 278 | + throw new ThingsKitException(ErrorMessage.INVALID_PARAMETER); |
244 | } | 279 | } |
280 | + try{ | ||
245 | Workbook work = WorkbookFactory.create(file.getInputStream()); | 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 | Sheet sheet = work.getSheetAt(0); | 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 | int succeed = 0;//成功数量 | 452 | int succeed = 0;//成功数量 |
248 | int failed =0;//失败数量 | 453 | int failed =0;//失败数量 |
249 | StringBuffer failedString = new StringBuffer(); | 454 | StringBuffer failedString = new StringBuffer(); |
@@ -253,41 +458,39 @@ public class ThingsModelController extends BaseController { | @@ -253,41 +458,39 @@ public class ThingsModelController extends BaseController { | ||
253 | String functionName = row.getCell(0).toString();//功能名称 | 458 | String functionName = row.getCell(0).toString();//功能名称 |
254 | if(StringUtils.isEmpty(functionName)){ | 459 | if(StringUtils.isEmpty(functionName)){ |
255 | failed++; | 460 | failed++; |
256 | - failedString.append("第"+i+"条,功能名称未填\r\n"); | 461 | + failedString.append("第"+i+"条,功能名称未填;"); |
257 | continue; | 462 | continue; |
258 | } | 463 | } |
259 | String identifier = row.getCell(1).toString();//标识符* | 464 | String identifier = row.getCell(1).toString();//标识符* |
260 | if(StringUtils.isEmpty(identifier)){ | 465 | if(StringUtils.isEmpty(identifier)){ |
261 | failed++; | 466 | failed++; |
262 | - failedString.append("第"+i+"条,标识符未填\r\n"); | 467 | + failedString.append("第"+i+"条,标识符未填;"); |
263 | continue; | 468 | continue; |
264 | } | 469 | } |
265 | String dataType = row.getCell(2).toString();//数据类型* | 470 | String dataType = row.getCell(2).toString();//数据类型* |
266 | if(StringUtils.isEmpty(dataType)){ | 471 | if(StringUtils.isEmpty(dataType)){ |
267 | failed++; | 472 | failed++; |
268 | - failedString.append("第"+i+"条,数据类型未填\r\n"); | 473 | + failedString.append("第"+i+"条,数据类型未填;"); |
269 | continue; | 474 | continue; |
270 | } | 475 | } |
271 | String accessMode = row.getCell(7).toString();//读写类型* | 476 | String accessMode = row.getCell(7).toString();//读写类型* |
272 | if(StringUtils.isEmpty(accessMode)){ | 477 | if(StringUtils.isEmpty(accessMode)){ |
273 | failed++; | 478 | failed++; |
274 | - failedString.append("第"+i+"条,读写类型未填\r\n"); | 479 | + failedString.append("第"+i+"条,读写类型未填;"); |
275 | continue; | 480 | continue; |
276 | } | 481 | } |
277 | Cell dataJons = row.getCell(9);//Josn对象 | 482 | Cell dataJons = row.getCell(9);//Josn对象 |
278 | if(dataType.equals("struct(结构体)")){ | 483 | if(dataType.equals("struct(结构体)")){ |
279 | if(StringUtils.isEmpty(dataJons.toString())){ | 484 | if(StringUtils.isEmpty(dataJons.toString())){ |
280 | failed++; | 485 | failed++; |
281 | - failedString.append("第"+i+"条,如果数据类型选为结构体则必填Json对象,请填入\r\n"); | 486 | + failedString.append("第"+i+"条,如果数据类型选为结构体则必填Json对象,请填入;"); |
282 | continue; | 487 | continue; |
283 | } | 488 | } |
284 | } | 489 | } |
285 | - | ||
286 | - String tenantId = getCurrentUser().getCurrentTenantId(); | ||
287 | Boolean identifierState = thingsModelService.getByIdentifier(tenantId,deviceProfileId,categoryId,identifier); | 490 | Boolean identifierState = thingsModelService.getByIdentifier(tenantId,deviceProfileId,categoryId,identifier); |
288 | if(!identifierState){ | 491 | if(!identifierState){ |
289 | failed++; | 492 | failed++; |
290 | - failedString.append("第"+i+"条,标识符已存在\r\n"); | 493 | + failedString.append("第"+i+"条,标识符已存在;"); |
291 | continue; | 494 | continue; |
292 | } | 495 | } |
293 | ThingsModelDTO dto = new ThingsModelDTO(); | 496 | ThingsModelDTO dto = new ThingsModelDTO(); |
@@ -349,32 +552,4 @@ public class ThingsModelController extends BaseController { | @@ -349,32 +552,4 @@ public class ThingsModelController extends BaseController { | ||
349 | } | 552 | } |
350 | return ResponseResult.success("操作成功,导入"+succeed+"条;失败"+failed+"条;失败原因:"+failedString.toString()); | 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,7 +5,6 @@ import io.swagger.annotations.ApiOperation; | ||
5 | import io.swagger.annotations.ApiParam; | 5 | import io.swagger.annotations.ApiParam; |
6 | import lombok.RequiredArgsConstructor; | 6 | import lombok.RequiredArgsConstructor; |
7 | import org.springframework.http.ResponseEntity; | 7 | import org.springframework.http.ResponseEntity; |
8 | -import org.springframework.security.access.prepost.PreAuthorize; | ||
9 | import org.springframework.validation.annotation.Validated; | 8 | import org.springframework.validation.annotation.Validated; |
10 | import org.springframework.web.bind.annotation.*; | 9 | import org.springframework.web.bind.annotation.*; |
11 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 10 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
@@ -65,7 +64,6 @@ public class TkDataViewInterfaceController extends BaseController { | @@ -65,7 +64,6 @@ public class TkDataViewInterfaceController extends BaseController { | ||
65 | 64 | ||
66 | @PostMapping | 65 | @PostMapping |
67 | @ApiOperation("新增") | 66 | @ApiOperation("新增") |
68 | - @PreAuthorize("@check.checkPermissions({},{})") | ||
69 | public ResponseEntity<TkDataViewInterfaceDTO> save( | 67 | public ResponseEntity<TkDataViewInterfaceDTO> save( |
70 | @Validated({AddGroup.class}) @RequestBody TkDataViewInterfaceDTO tkDataViewInterfaceDTO) | 68 | @Validated({AddGroup.class}) @RequestBody TkDataViewInterfaceDTO tkDataViewInterfaceDTO) |
71 | throws ThingsboardException { | 69 | throws ThingsboardException { |
@@ -77,7 +75,6 @@ public class TkDataViewInterfaceController extends BaseController { | @@ -77,7 +75,6 @@ public class TkDataViewInterfaceController extends BaseController { | ||
77 | 75 | ||
78 | @PutMapping | 76 | @PutMapping |
79 | @ApiOperation("修改") | 77 | @ApiOperation("修改") |
80 | - @PreAuthorize("@check.checkPermissions({},{})") | ||
81 | public ResponseEntity<TkDataViewInterfaceDTO> update( | 78 | public ResponseEntity<TkDataViewInterfaceDTO> update( |
82 | @Validated({UpdateGroup.class}) @RequestBody TkDataViewInterfaceDTO TkDataViewInterfaceDTO) | 79 | @Validated({UpdateGroup.class}) @RequestBody TkDataViewInterfaceDTO TkDataViewInterfaceDTO) |
83 | throws ThingsboardException { | 80 | throws ThingsboardException { |
@@ -88,7 +85,6 @@ public class TkDataViewInterfaceController extends BaseController { | @@ -88,7 +85,6 @@ public class TkDataViewInterfaceController extends BaseController { | ||
88 | 85 | ||
89 | @DeleteMapping | 86 | @DeleteMapping |
90 | @ApiOperation("删除") | 87 | @ApiOperation("删除") |
91 | - @PreAuthorize("@check.checkPermissions({},{})") | ||
92 | public ResponseEntity<Boolean> delete( | 88 | public ResponseEntity<Boolean> delete( |
93 | @Validated({DeleteGroup.class}) @RequestBody DeleteDTO deleteDTO) | 89 | @Validated({DeleteGroup.class}) @RequestBody DeleteDTO deleteDTO) |
94 | throws ThingsboardException { | 90 | throws ThingsboardException { |
@@ -98,7 +94,6 @@ public class TkDataViewInterfaceController extends BaseController { | @@ -98,7 +94,6 @@ public class TkDataViewInterfaceController extends BaseController { | ||
98 | 94 | ||
99 | @GetMapping("/publish/{id}") | 95 | @GetMapping("/publish/{id}") |
100 | @ApiOperation("发布") | 96 | @ApiOperation("发布") |
101 | - @PreAuthorize("@check.checkPermissions({},{})") | ||
102 | public ResponseEntity<Boolean> publishInterface(@PathVariable("id") String id) | 97 | public ResponseEntity<Boolean> publishInterface(@PathVariable("id") String id) |
103 | throws ThingsboardException { | 98 | throws ThingsboardException { |
104 | return ResponseEntity.ok( | 99 | return ResponseEntity.ok( |
@@ -107,7 +102,6 @@ public class TkDataViewInterfaceController extends BaseController { | @@ -107,7 +102,6 @@ public class TkDataViewInterfaceController extends BaseController { | ||
107 | 102 | ||
108 | @GetMapping("/cancel_publish/{id}") | 103 | @GetMapping("/cancel_publish/{id}") |
109 | @ApiOperation("取消发布") | 104 | @ApiOperation("取消发布") |
110 | - @PreAuthorize("@check.checkPermissions({},{})") | ||
111 | public ResponseEntity<Boolean> cancelPublishInterface(@PathVariable("id") String id) | 105 | public ResponseEntity<Boolean> cancelPublishInterface(@PathVariable("id") String id) |
112 | throws ThingsboardException { | 106 | throws ThingsboardException { |
113 | return ResponseEntity.ok( | 107 | return ResponseEntity.ok( |
@@ -116,7 +110,6 @@ public class TkDataViewInterfaceController extends BaseController { | @@ -116,7 +110,6 @@ public class TkDataViewInterfaceController extends BaseController { | ||
116 | } | 110 | } |
117 | 111 | ||
118 | @ApiOperation("根据接口ID,获得接口详情") | 112 | @ApiOperation("根据接口ID,获得接口详情") |
119 | - @PreAuthorize("@check.checkPermissions({},{})") | ||
120 | @RequestMapping( | 113 | @RequestMapping( |
121 | value = "/get_interface_details", | 114 | value = "/get_interface_details", |
122 | params = {"ids"}, | 115 | params = {"ids"}, |
@@ -130,7 +123,6 @@ public class TkDataViewInterfaceController extends BaseController { | @@ -130,7 +123,6 @@ public class TkDataViewInterfaceController extends BaseController { | ||
130 | 123 | ||
131 | @GetMapping("/filter_by_interface_type/{type}") | 124 | @GetMapping("/filter_by_interface_type/{type}") |
132 | @ApiOperation("根据接口类型过滤数据") | 125 | @ApiOperation("根据接口类型过滤数据") |
133 | - @PreAuthorize("@check.checkPermissions({},{})") | ||
134 | public List<TkDataViewInterfaceDTO> filterByInterfaceType(@PathVariable("type") String type) | 126 | public List<TkDataViewInterfaceDTO> filterByInterfaceType(@PathVariable("type") String type) |
135 | throws ThingsboardException { | 127 | throws ThingsboardException { |
136 | return tkDataViewInterfaceService.filterByInterfaceType( | 128 | return tkDataViewInterfaceService.filterByInterfaceType( |
@@ -139,7 +131,6 @@ public class TkDataViewInterfaceController extends BaseController { | @@ -139,7 +131,6 @@ public class TkDataViewInterfaceController extends BaseController { | ||
139 | 131 | ||
140 | @GetMapping("/find_all_interface/{state}") | 132 | @GetMapping("/find_all_interface/{state}") |
141 | @ApiOperation("查询所有接口") | 133 | @ApiOperation("查询所有接口") |
142 | - @PreAuthorize("@check.checkPermissions({},{})") | ||
143 | public List<TkDataViewInterfaceDTO> findAll(@PathVariable("state") String state) | 134 | public List<TkDataViewInterfaceDTO> findAll(@PathVariable("state") String state) |
144 | throws ThingsboardException { | 135 | throws ThingsboardException { |
145 | return tkDataViewInterfaceService.findAll(state, getCurrentUser().getCurrentTenantId()); | 136 | return tkDataViewInterfaceService.findAll(state, getCurrentUser().getCurrentTenantId()); |
@@ -147,7 +138,6 @@ public class TkDataViewInterfaceController extends BaseController { | @@ -147,7 +138,6 @@ public class TkDataViewInterfaceController extends BaseController { | ||
147 | 138 | ||
148 | @GetMapping("/find/can_use_interfaces") | 139 | @GetMapping("/find/can_use_interfaces") |
149 | @ApiOperation("查询当前用户可使用的接口") | 140 | @ApiOperation("查询当前用户可使用的接口") |
150 | - @PreAuthorize("@check.checkPermissions({},{})") | ||
151 | public List<TkDataViewInterfaceDTO> findCanUseInterfaces() throws ThingsboardException { | 141 | public List<TkDataViewInterfaceDTO> findCanUseInterfaces() throws ThingsboardException { |
152 | boolean isSysAdminOrPtAdmin = getCurrentUser().isPtAdmin() || getCurrentUser().isSystemAdmin(); | 142 | boolean isSysAdminOrPtAdmin = getCurrentUser().isPtAdmin() || getCurrentUser().isSystemAdmin(); |
153 | return tkDataViewInterfaceService.findCanUseInterfaces( | 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,10 +33,7 @@ import org.thingsboard.server.common.data.yunteng.constant.FastIotConstants; | ||
33 | import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; | 33 | import org.thingsboard.server.common.data.yunteng.core.exception.TkDataValidationException; |
34 | import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; | 34 | import org.thingsboard.server.common.data.yunteng.core.message.ErrorMessage; |
35 | import org.thingsboard.server.common.data.yunteng.dto.*; | 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 | import org.thingsboard.server.common.data.yunteng.utils.tools.ResponseResult; | 37 | import org.thingsboard.server.common.data.yunteng.utils.tools.ResponseResult; |
41 | import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; | 38 | import org.thingsboard.server.common.data.yunteng.utils.tools.TkPageData; |
42 | import org.thingsboard.server.controller.BaseController; | 39 | import org.thingsboard.server.controller.BaseController; |
@@ -225,7 +222,7 @@ public class TkDeviceController extends BaseController { | @@ -225,7 +222,7 @@ public class TkDeviceController extends BaseController { | ||
225 | 222 | ||
226 | @GetMapping("{id}") | 223 | @GetMapping("{id}") |
227 | @ApiOperation("详情") | 224 | @ApiOperation("详情") |
228 | - @PreAuthorize("@check.checkPermissions({},{'api:yt:device:get'})") | 225 | + @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:device:get'})") |
229 | public ResponseEntity<DeviceDTO> getDevice(@PathVariable("id") String id) | 226 | public ResponseEntity<DeviceDTO> getDevice(@PathVariable("id") String id) |
230 | throws ThingsboardException { | 227 | throws ThingsboardException { |
231 | return ResponseEntity.of(tkdeviceService.getDevice(getCurrentUser().getCurrentTenantId(), id)); | 228 | return ResponseEntity.of(tkdeviceService.getDevice(getCurrentUser().getCurrentTenantId(), id)); |
@@ -239,7 +236,7 @@ public class TkDeviceController extends BaseController { | @@ -239,7 +236,7 @@ public class TkDeviceController extends BaseController { | ||
239 | return ResponseEntity.of(tkdeviceService.getDevice(getCurrentUser().getCurrentTenantId(), id)); | 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 | @PostMapping(params = {PAGE_SIZE, PAGE}) | 240 | @PostMapping(params = {PAGE_SIZE, PAGE}) |
244 | @ApiOperation("查询") | 241 | @ApiOperation("查询") |
245 | public TkPageData<DeviceDTO> pageDevice( | 242 | public TkPageData<DeviceDTO> pageDevice( |
@@ -265,7 +262,7 @@ public class TkDeviceController extends BaseController { | @@ -265,7 +262,7 @@ public class TkDeviceController extends BaseController { | ||
265 | return tkdeviceService.page(getCurrentUser().getCurrentTenantId(), queryMap); | 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 | @GetMapping( | 266 | @GetMapping( |
270 | path = {"/relation"}, | 267 | path = {"/relation"}, |
271 | params = {PAGE_SIZE, PAGE}) | 268 | params = {PAGE_SIZE, PAGE}) |
@@ -383,14 +380,16 @@ public class TkDeviceController extends BaseController { | @@ -383,14 +380,16 @@ public class TkDeviceController extends BaseController { | ||
383 | @ApiParam(value = "设备标签") @RequestParam(value = "deviceLabel", required = false) | 380 | @ApiParam(value = "设备标签") @RequestParam(value = "deviceLabel", required = false) |
384 | String deviceLabel, | 381 | String deviceLabel, |
385 | @ApiParam(value = "设备配置ID") @RequestParam(value = "deviceProfileId", required = false) | 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 | throws ThingsboardException { | 386 | throws ThingsboardException { |
388 | return tkdeviceService.findDevicesByDeviceTypeAndOrganizationId( | 387 | return tkdeviceService.findDevicesByDeviceTypeAndOrganizationId( |
389 | deviceType, | 388 | deviceType, |
390 | getCurrentUser().getCurrentTenantId(), | 389 | getCurrentUser().getCurrentTenantId(), |
391 | organizationId, | 390 | organizationId, |
392 | deviceLabel, | 391 | deviceLabel, |
393 | - deviceProfileId); | 392 | + deviceProfileId,transportType); |
394 | } | 393 | } |
395 | 394 | ||
396 | 395 | ||
@@ -539,6 +538,12 @@ public class TkDeviceController extends BaseController { | @@ -539,6 +538,12 @@ public class TkDeviceController extends BaseController { | ||
539 | tbDevice.setName(deviceDTO.getName()); | 538 | tbDevice.setName(deviceDTO.getName()); |
540 | tbDevice.setCreatedTime(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()); | 539 | tbDevice.setCreatedTime(LocalDateTime.now().toInstant(ZoneOffset.of("+8")).toEpochMilli()); |
541 | tbDevice.setTenantId(tenantId); | 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 | return tbDevice; | 547 | return tbDevice; |
543 | } | 548 | } |
544 | 549 |
@@ -14,6 +14,7 @@ import org.thingsboard.server.common.data.audit.ActionType; | @@ -14,6 +14,7 @@ import org.thingsboard.server.common.data.audit.ActionType; | ||
14 | import org.thingsboard.server.common.data.edge.EdgeEventActionType; | 14 | import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
15 | import org.thingsboard.server.common.data.exception.ThingsboardException; | 15 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
16 | import org.thingsboard.server.common.data.id.DeviceProfileId; | 16 | import org.thingsboard.server.common.data.id.DeviceProfileId; |
17 | +import org.thingsboard.server.common.data.id.OtaPackageId; | ||
17 | import org.thingsboard.server.common.data.id.RuleChainId; | 18 | import org.thingsboard.server.common.data.id.RuleChainId; |
18 | import org.thingsboard.server.common.data.id.TenantId; | 19 | import org.thingsboard.server.common.data.id.TenantId; |
19 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; | 20 | import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
@@ -119,16 +120,16 @@ public class TkDeviceProfileController extends BaseController { | @@ -119,16 +120,16 @@ public class TkDeviceProfileController extends BaseController { | ||
119 | @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{})") | 120 | @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{})") |
120 | public ResponseEntity listDeviceProfile( | 121 | public ResponseEntity listDeviceProfile( |
121 | @ApiParam(value = "设备类型") @RequestParam(value = "deviceType", required = false) | 122 | @ApiParam(value = "设备类型") @RequestParam(value = "deviceType", required = false) |
122 | - DeviceTypeEnum deviceType) | 123 | + DeviceTypeEnum deviceType,DeviceTransportType transportType) |
123 | throws ThingsboardException { | 124 | throws ThingsboardException { |
124 | List<DeviceProfileDTO> results; | 125 | List<DeviceProfileDTO> results; |
125 | String tenantId = getCurrentUser().getCurrentTenantId(); | 126 | String tenantId = getCurrentUser().getCurrentTenantId(); |
126 | if (getCurrentUser().isTenantAdmin()) { | 127 | if (getCurrentUser().isTenantAdmin()) { |
127 | - results = tkDeviceProfileService.findDeviceProfile(tenantId, null,deviceType); | 128 | + results = tkDeviceProfileService.findDeviceProfile(tenantId, null,deviceType,transportType); |
128 | } else { | 129 | } else { |
129 | results = | 130 | results = |
130 | tkDeviceProfileService.findCustomerDeviceProfiles( | 131 | tkDeviceProfileService.findCustomerDeviceProfiles( |
131 | - tenantId, getCurrentUser().getCustomerId(),deviceType); | 132 | + tenantId, getCurrentUser().getCustomerId(),deviceType,transportType); |
132 | } | 133 | } |
133 | 134 | ||
134 | return ResponseEntity.ok(results); | 135 | return ResponseEntity.ok(results); |
@@ -257,6 +258,12 @@ public class TkDeviceProfileController extends BaseController { | @@ -257,6 +258,12 @@ public class TkDeviceProfileController extends BaseController { | ||
257 | 258 | ||
258 | tbDeviceProfile.setProfileData( | 259 | tbDeviceProfile.setProfileData( |
259 | buildDeviceProfileData(transportType, deviceProfileDTO.getProfileData())); | 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 | return tbDeviceProfile; | 267 | return tbDeviceProfile; |
261 | } | 268 | } |
262 | } | 269 | } |
@@ -114,7 +114,7 @@ public class TkDeviceScriptController extends BaseController { | @@ -114,7 +114,7 @@ public class TkDeviceScriptController extends BaseController { | ||
114 | 114 | ||
115 | @GetMapping(params = {PAGE_SIZE, PAGE}) | 115 | @GetMapping(params = {PAGE_SIZE, PAGE}) |
116 | @ApiOperation("分页查询") | 116 | @ApiOperation("分页查询") |
117 | - @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{})") | 117 | + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN','CUSTOMER_USER')") |
118 | public TkPageData<TkDeviceScriptDTO> pageDeviceScript( | 118 | public TkPageData<TkDeviceScriptDTO> pageDeviceScript( |
119 | @RequestParam(PAGE_SIZE) int pageSize, | 119 | @RequestParam(PAGE_SIZE) int pageSize, |
120 | @RequestParam(PAGE) int page, | 120 | @RequestParam(PAGE) int page, |
@@ -209,7 +209,7 @@ public class TkDeviceScriptController extends BaseController { | @@ -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 | @PostMapping("/modbus") | 213 | @PostMapping("/modbus") |
214 | @ApiOperation("生成Modbus指令") | 214 | @ApiOperation("生成Modbus指令") |
215 | public ResponseEntity<String> modbus(@RequestBody TkDeviceRpcDTO inputParams) | 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,6 +11,7 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; | ||
11 | import org.thingsboard.server.common.data.yunteng.common.DeleteGroup; | 11 | import org.thingsboard.server.common.data.yunteng.common.DeleteGroup; |
12 | import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO; | 12 | import org.thingsboard.server.common.data.yunteng.dto.DeleteDTO; |
13 | import org.thingsboard.server.common.data.yunteng.dto.TkVideoDTO; | 13 | import org.thingsboard.server.common.data.yunteng.dto.TkVideoDTO; |
14 | +import org.thingsboard.server.common.data.yunteng.dto.TkVideoGbtDTO; | ||
14 | import org.thingsboard.server.common.data.yunteng.enums.OrderTypeEnum; | 15 | import org.thingsboard.server.common.data.yunteng.enums.OrderTypeEnum; |
15 | import org.thingsboard.server.common.data.yunteng.utils.tools.ProtocolType; | 16 | import org.thingsboard.server.common.data.yunteng.utils.tools.ProtocolType; |
16 | import org.thingsboard.server.common.data.yunteng.utils.tools.ResponseResult; | 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,7 +31,7 @@ import static org.thingsboard.server.common.data.yunteng.constant.QueryConstant. | ||
30 | @RequestMapping("api/yt/video") | 31 | @RequestMapping("api/yt/video") |
31 | @Api(tags = {"视频流"}) | 32 | @Api(tags = {"视频流"}) |
32 | @RequiredArgsConstructor | 33 | @RequiredArgsConstructor |
33 | -@PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{})") | 34 | +@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
34 | public class TkVideoController extends BaseController { | 35 | public class TkVideoController extends BaseController { |
35 | 36 | ||
36 | private final TkVideoService videoService; | 37 | private final TkVideoService videoService; |
@@ -68,6 +69,17 @@ public class TkVideoController extends BaseController { | @@ -68,6 +69,17 @@ public class TkVideoController extends BaseController { | ||
68 | return videoService.saveOrUpdate(dto); | 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 | @DeleteMapping | 83 | @DeleteMapping |
72 | @ApiOperation("删除") | 84 | @ApiOperation("删除") |
73 | @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:video:delete'})") | 85 | @PreAuthorize("@check.checkPermissions({'TENANT_ADMIN','CUSTOMER_USER'},{'api:yt:video:delete'})") |
@@ -138,8 +150,6 @@ public class TkVideoController extends BaseController { | @@ -138,8 +150,6 @@ public class TkVideoController extends BaseController { | ||
138 | } | 150 | } |
139 | String url = | 151 | String url = |
140 | videoService.getCameraPreviewURL( | 152 | videoService.getCameraPreviewURL( |
141 | - getCurrentUser().isTenantAdmin(), | ||
142 | - getCurrentUser().getCurrentUserId(), | ||
143 | getCurrentUser().getCurrentTenantId(), | 153 | getCurrentUser().getCurrentTenantId(), |
144 | entityId, | 154 | entityId, |
145 | protocolType); | 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,6 +15,9 @@ | ||
15 | */ | 15 | */ |
16 | package org.thingsboard.server.service.transport; | 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 | import com.fasterxml.jackson.core.JsonProcessingException; | 21 | import com.fasterxml.jackson.core.JsonProcessingException; |
19 | import com.fasterxml.jackson.databind.JsonNode; | 22 | import com.fasterxml.jackson.databind.JsonNode; |
20 | import com.fasterxml.jackson.databind.ObjectMapper; | 23 | import com.fasterxml.jackson.databind.ObjectMapper; |
@@ -23,8 +26,18 @@ import com.google.common.util.concurrent.Futures; | @@ -23,8 +26,18 @@ import com.google.common.util.concurrent.Futures; | ||
23 | import com.google.common.util.concurrent.ListenableFuture; | 26 | import com.google.common.util.concurrent.ListenableFuture; |
24 | import com.google.common.util.concurrent.MoreExecutors; | 27 | import com.google.common.util.concurrent.MoreExecutors; |
25 | import com.google.protobuf.ByteString; | 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 | import lombok.RequiredArgsConstructor; | 38 | import lombok.RequiredArgsConstructor; |
27 | import lombok.extern.slf4j.Slf4j; | 39 | import lombok.extern.slf4j.Slf4j; |
40 | +import org.apache.zookeeper.Op; | ||
28 | import org.springframework.stereotype.Service; | 41 | import org.springframework.stereotype.Service; |
29 | import org.thingsboard.common.util.JacksonUtil; | 42 | import org.thingsboard.common.util.JacksonUtil; |
30 | import org.thingsboard.server.cache.ota.OtaPackageDataCache; | 43 | import org.thingsboard.server.cache.ota.OtaPackageDataCache; |
@@ -60,9 +73,14 @@ import org.thingsboard.server.common.data.page.PageLink; | @@ -60,9 +73,14 @@ import org.thingsboard.server.common.data.page.PageLink; | ||
60 | import org.thingsboard.server.common.data.relation.EntityRelation; | 73 | import org.thingsboard.server.common.data.relation.EntityRelation; |
61 | import org.thingsboard.server.common.data.security.DeviceCredentials; | 74 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
62 | import org.thingsboard.server.common.data.security.DeviceCredentialsType; | 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 | import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO; | 78 | import org.thingsboard.server.common.data.yunteng.dto.DeviceDTO; |
64 | import org.thingsboard.server.common.data.yunteng.dto.TkDeviceScriptDTO; | 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 | import org.thingsboard.server.common.msg.EncryptionUtil; | 84 | import org.thingsboard.server.common.msg.EncryptionUtil; |
67 | import org.thingsboard.server.common.msg.TbMsg; | 85 | import org.thingsboard.server.common.msg.TbMsg; |
68 | import org.thingsboard.server.common.msg.TbMsgDataType; | 86 | import org.thingsboard.server.common.msg.TbMsgDataType; |
@@ -77,8 +95,11 @@ import org.thingsboard.server.dao.device.provision.ProvisionResponse; | @@ -77,8 +95,11 @@ import org.thingsboard.server.dao.device.provision.ProvisionResponse; | ||
77 | import org.thingsboard.server.dao.ota.OtaPackageService; | 95 | import org.thingsboard.server.dao.ota.OtaPackageService; |
78 | import org.thingsboard.server.dao.relation.RelationService; | 96 | import org.thingsboard.server.dao.relation.RelationService; |
79 | import org.thingsboard.server.dao.tenant.TbTenantProfileCache; | 97 | import org.thingsboard.server.dao.tenant.TbTenantProfileCache; |
98 | +import org.thingsboard.server.dao.yunteng.entities.TkVideoChannelEntity; | ||
80 | import org.thingsboard.server.dao.yunteng.service.TkDeviceScriptService; | 99 | import org.thingsboard.server.dao.yunteng.service.TkDeviceScriptService; |
81 | import org.thingsboard.server.dao.yunteng.service.TkDeviceService; | 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 | import org.thingsboard.server.gen.transport.TransportProtos; | 103 | import org.thingsboard.server.gen.transport.TransportProtos; |
83 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; | 104 | import org.thingsboard.server.gen.transport.TransportProtos.DeviceInfoProto; |
84 | import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceCredentialsRequestMsg; | 105 | import org.thingsboard.server.gen.transport.TransportProtos.GetDeviceCredentialsRequestMsg; |
@@ -105,19 +126,6 @@ import org.thingsboard.server.service.install.DefaultSystemDataLoaderService; | @@ -105,19 +126,6 @@ import org.thingsboard.server.service.install.DefaultSystemDataLoaderService; | ||
105 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; | 126 | import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
106 | import org.thingsboard.server.service.resource.TbResourceService; | 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 | * Created by ashvayka on 05.10.18. | 130 | * Created by ashvayka on 05.10.18. |
123 | */ | 131 | */ |
@@ -135,6 +143,10 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -135,6 +143,10 @@ public class DefaultTransportApiService implements TransportApiService { | ||
135 | private final DeviceService deviceService; | 143 | private final DeviceService deviceService; |
136 | private final TkDeviceService ytDeviceService; | 144 | private final TkDeviceService ytDeviceService; |
137 | private final TkDeviceScriptService scriptService; | 145 | private final TkDeviceScriptService scriptService; |
146 | + private final TkVideoChannelService channelService; | ||
147 | + private final TkMediaServerNodeService mediaServerNodeService; | ||
148 | + private final VideoStreamSessionManager videoStreamSessionManager; | ||
149 | + | ||
138 | private final RelationService relationService; | 150 | private final RelationService relationService; |
139 | private final DeviceCredentialsService deviceCredentialsService; | 151 | private final DeviceCredentialsService deviceCredentialsService; |
140 | private final DbCallbackExecutorService dbCallbackExecutorService; | 152 | private final DbCallbackExecutorService dbCallbackExecutorService; |
@@ -192,6 +204,9 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -192,6 +204,9 @@ public class DefaultTransportApiService implements TransportApiService { | ||
192 | else if (transportApiRequestMsg.hasScript()) { | 204 | else if (transportApiRequestMsg.hasScript()) { |
193 | result = handle(transportApiRequestMsg.getScript()); | 205 | result = handle(transportApiRequestMsg.getScript()); |
194 | } | 206 | } |
207 | + else if (transportApiRequestMsg.hasGbt28181RequestMsg()) { | ||
208 | + result = handleGbt(transportApiRequestMsg.getGbt28181RequestMsg()); | ||
209 | + } | ||
195 | 210 | ||
196 | 211 | ||
197 | return Futures.transform(Optional.ofNullable(result).orElseGet(this::getEmptyTransportApiResponseFuture), | 212 | return Futures.transform(Optional.ofNullable(result).orElseGet(this::getEmptyTransportApiResponseFuture), |
@@ -685,20 +700,76 @@ public class DefaultTransportApiService implements TransportApiService { | @@ -685,20 +700,76 @@ public class DefaultTransportApiService implements TransportApiService { | ||
685 | private ListenableFuture<TransportApiResponseMsg> handle(TransportProtos.ScriptProto requestMsg) { | 700 | private ListenableFuture<TransportApiResponseMsg> handle(TransportProtos.ScriptProto requestMsg) { |
686 | List<TkDeviceScriptDTO> allScriptes = scriptService.getScriptes(); | 701 | List<TkDeviceScriptDTO> allScriptes = scriptService.getScriptes(); |
687 | TransportApiResponseMsg.Builder responseBuilder = TransportApiResponseMsg.newBuilder(); | 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 | return Futures.immediateFuture(responseBuilder.build()); | 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,6 +451,12 @@ caffeine: | ||
451 | sceneReact: | 451 | sceneReact: |
452 | timeToLiveInMinutes: "${CACHE_SPECS_SCENE_TTL:1440}" | 452 | timeToLiveInMinutes: "${CACHE_SPECS_SCENE_TTL:1440}" |
453 | maxSize: "${CACHE_SPECS_SCENE_MAX_SIZE:10000}" | 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 | redis: | 460 | redis: |
455 | # standalone or cluster | 461 | # standalone or cluster |
456 | connection: | 462 | connection: |
@@ -476,7 +482,7 @@ redis: | @@ -476,7 +482,7 @@ redis: | ||
476 | # db index | 482 | # db index |
477 | db: "${REDIS_DB:0}" | 483 | db: "${REDIS_DB:0}" |
478 | # db password | 484 | # db password |
479 | - password: "${REDIS_PASSWORD:}" | 485 | + password: "${REDIS_PASSWORD:redis@6379}" |
480 | # pool config | 486 | # pool config |
481 | pool_config: | 487 | pool_config: |
482 | maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}" | 488 | maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}" |
@@ -712,6 +718,9 @@ transport: | @@ -712,6 +718,9 @@ transport: | ||
712 | # Skip certificate validity check for client certificates. | 718 | # Skip certificate validity check for client certificates. |
713 | skip_validity_check_for_client_cert: "${MQTT_SSL_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}" | 719 | skip_validity_check_for_client_cert: "${MQTT_SSL_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}" |
714 | # Local CoAP transport parameters | 720 | # Local CoAP transport parameters |
721 | + gbt28181: | ||
722 | + # Enable/disable gbt28181 transport protocol. | ||
723 | + enabled: "${GBT28181_ENABLED:false}" | ||
715 | tcp: | 724 | tcp: |
716 | # Enable/disable tcp transport protocol. | 725 | # Enable/disable tcp transport protocol. |
717 | enabled: "${TCP_ENABLED:true}" | 726 | enabled: "${TCP_ENABLED:true}" |
@@ -1218,3 +1227,45 @@ logging: | @@ -1218,3 +1227,45 @@ logging: | ||
1218 | frp: | 1227 | frp: |
1219 | server: | 1228 | server: |
1220 | address: ${FRP_SERVER_ADDRESS:http://127.0.0.1} | 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,7 +8,7 @@ | ||
8 | * http://www.apache.org/licenses/LICENSE-2.0 | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * | 9 | * |
10 | * Unless required by applicable law or agreed to in writing, software | 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 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and | 13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. | 14 | * limitations under the License. |
@@ -130,10 +130,7 @@ message SessionEventMsg { | @@ -130,10 +130,7 @@ message SessionEventMsg { | ||
130 | SessionEvent event = 2; | 130 | SessionEvent event = 2; |
131 | } | 131 | } |
132 | 132 | ||
133 | -//thingskit | ||
134 | -message PostEventMsg { | ||
135 | - repeated KeyValueProto kv = 1; | ||
136 | -} | 133 | + |
137 | message PostTelemetryMsg { | 134 | message PostTelemetryMsg { |
138 | repeated TsKvListProto tsKvList = 1; | 135 | repeated TsKvListProto tsKvList = 1; |
139 | } | 136 | } |
@@ -682,6 +679,7 @@ message TransportApiRequestMsg { | @@ -682,6 +679,7 @@ message TransportApiRequestMsg { | ||
682 | 679 | ||
683 | //Thingskit function | 680 | //Thingskit function |
684 | ScriptProto script = 14; | 681 | ScriptProto script = 14; |
682 | + Gbt28181RequestMsg gbt28181RequestMsg = 15; | ||
685 | } | 683 | } |
686 | 684 | ||
687 | /* Response from ThingsBoard Core Service to Transport Service */ | 685 | /* Response from ThingsBoard Core Service to Transport Service */ |
@@ -699,6 +697,7 @@ message TransportApiResponseMsg { | @@ -699,6 +697,7 @@ message TransportApiResponseMsg { | ||
699 | 697 | ||
700 | //Thingskit function | 698 | //Thingskit function |
701 | repeated ScriptProto scriptsResponseMsg = 11; | 699 | repeated ScriptProto scriptsResponseMsg = 11; |
700 | + Gbt28181ResponseMsg gbt28181ResponseMsg = 12; | ||
702 | } | 701 | } |
703 | 702 | ||
704 | /* Messages that are handled by ThingsBoard Core Service */ | 703 | /* Messages that are handled by ThingsBoard Core Service */ |
@@ -795,3 +794,37 @@ message ScriptProto{ | @@ -795,3 +794,37 @@ message ScriptProto{ | ||
795 | string convertJs = 6; | 794 | string convertJs = 6; |
796 | int32 status = 7; | 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,6 +48,8 @@ public class DataConstants { | ||
48 | public static final String HTTP_TRANSPORT_NAME = "HTTP"; | 48 | public static final String HTTP_TRANSPORT_NAME = "HTTP"; |
49 | public static final String SNMP_TRANSPORT_NAME = "SNMP"; | 49 | public static final String SNMP_TRANSPORT_NAME = "SNMP"; |
50 | 50 | ||
51 | + public static final String GBT_28181 = "GBT28181"; | ||
52 | + | ||
51 | 53 | ||
52 | public static final String[] allScopes() { | 54 | public static final String[] allScopes() { |
53 | return new String[]{CLIENT_SCOPE, SHARED_SCOPE, SERVER_SCOPE}; | 55 | return new String[]{CLIENT_SCOPE, SHARED_SCOPE, SERVER_SCOPE}; |
@@ -34,6 +34,7 @@ import java.io.Serializable; | @@ -34,6 +34,7 @@ import java.io.Serializable; | ||
34 | @JsonSubTypes.Type(value = DefaultDeviceTransportConfiguration.class, name = "DEFAULT"), | 34 | @JsonSubTypes.Type(value = DefaultDeviceTransportConfiguration.class, name = "DEFAULT"), |
35 | @JsonSubTypes.Type(value = MqttDeviceTransportConfiguration.class, name = "MQTT"), | 35 | @JsonSubTypes.Type(value = MqttDeviceTransportConfiguration.class, name = "MQTT"), |
36 | @JsonSubTypes.Type(value = TkTcpDeviceTransportConfiguration.class, name = "TCP"), | 36 | @JsonSubTypes.Type(value = TkTcpDeviceTransportConfiguration.class, name = "TCP"), |
37 | + @JsonSubTypes.Type(value = TkGBTDeviceTransportConfiguration.class, name = "GBT28181"), | ||
37 | @JsonSubTypes.Type(value = CoapDeviceTransportConfiguration.class, name = "COAP"), | 38 | @JsonSubTypes.Type(value = CoapDeviceTransportConfiguration.class, name = "COAP"), |
38 | @JsonSubTypes.Type(value = Lwm2mDeviceTransportConfiguration.class, name = "LWM2M"), | 39 | @JsonSubTypes.Type(value = Lwm2mDeviceTransportConfiguration.class, name = "LWM2M"), |
39 | @JsonSubTypes.Type(value = SnmpDeviceTransportConfiguration.class, name = "SNMP")}) | 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,6 +32,7 @@ import java.io.Serializable; | ||
32 | @JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"), | 32 | @JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"), |
33 | @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"), | 33 | @JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"), |
34 | @JsonSubTypes.Type(value = TkTcpDeviceProfileTransportConfiguration.class, name = "TCP"), | 34 | @JsonSubTypes.Type(value = TkTcpDeviceProfileTransportConfiguration.class, name = "TCP"), |
35 | + @JsonSubTypes.Type(value = TkGBT28181DeviceProfileTransportConfiguration.class, name = "GBT28181"), | ||
35 | @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M"), | 36 | @JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M"), |
36 | @JsonSubTypes.Type(value = CoapDeviceProfileTransportConfiguration.class, name = "COAP"), | 37 | @JsonSubTypes.Type(value = CoapDeviceProfileTransportConfiguration.class, name = "COAP"), |
37 | @JsonSubTypes.Type(value = SnmpDeviceProfileTransportConfiguration.class, name = "SNMP") | 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,6 +3,7 @@ package org.thingsboard.server.common.data.device.profile; | ||
3 | import lombok.Data; | 3 | import lombok.Data; |
4 | import org.thingsboard.server.common.data.DeviceTransportType; | 4 | import org.thingsboard.server.common.data.DeviceTransportType; |
5 | import org.thingsboard.server.common.data.validation.NoXss; | 5 | import org.thingsboard.server.common.data.validation.NoXss; |
6 | +import org.thingsboard.server.common.data.yunteng.enums.ProtocolAnalysisEnum; | ||
6 | 7 | ||
7 | @Data | 8 | @Data |
8 | public class TkTcpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { | 9 | public class TkTcpDeviceProfileTransportConfiguration implements DeviceProfileTransportConfiguration { |
@@ -26,6 +27,12 @@ public class TkTcpDeviceProfileTransportConfiguration implements DeviceProfileTr | @@ -26,6 +27,12 @@ public class TkTcpDeviceProfileTransportConfiguration implements DeviceProfileTr | ||
26 | @NoXss | 27 | @NoXss |
27 | private String downScriptId; | 28 | private String downScriptId; |
28 | 29 | ||
30 | + /** | ||
31 | + * 协议: 默认自定义 | ||
32 | + */ | ||
33 | + @NoXss | ||
34 | + private ProtocolAnalysisEnum protocol = ProtocolAnalysisEnum.CUSTOM; | ||
35 | + | ||
29 | @Override | 36 | @Override |
30 | public DeviceTransportType getType() { | 37 | public DeviceTransportType getType() { |
31 | return DeviceTransportType.TCP; | 38 | return DeviceTransportType.TCP; |
1 | package org.thingsboard.server.common.data.yunteng.common; | 1 | package org.thingsboard.server.common.data.yunteng.common; |
2 | 2 | ||
3 | import org.thingsboard.server.common.data.yunteng.dto.SysDictItemDTO; | 3 | import org.thingsboard.server.common.data.yunteng.dto.SysDictItemDTO; |
4 | +import org.thingsboard.server.common.data.yunteng.dto.sip.MediaServerDTO; | ||
4 | 5 | ||
5 | public interface TkCommonService { | 6 | public interface TkCommonService { |
6 | /** | 7 | /** |
@@ -11,6 +12,4 @@ public interface TkCommonService { | @@ -11,6 +12,4 @@ public interface TkCommonService { | ||
11 | * @return 返回字典Item表 | 12 | * @return 返回字典Item表 |
12 | */ | 13 | */ |
13 | SysDictItemDTO getDictValueByCodeAndText(String dictCode, String codeValue); | 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,6 +37,175 @@ public interface FastIotConstants { | ||
37 | String ORGANIZATION = "organization"; | 37 | String ORGANIZATION = "organization"; |
38 | String SCENE_REACT = "sceneReact"; | 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 | interface TBCacheConfig { | 210 | interface TBCacheConfig { |
42 | String TB_CACHE_CONFIG_KEY = "TB_CONNECT_CACHE"; | 211 | String TB_CACHE_CONFIG_KEY = "TB_CONNECT_CACHE"; |
@@ -66,6 +235,23 @@ public interface FastIotConstants { | @@ -66,6 +235,23 @@ public interface FastIotConstants { | ||
66 | String NODE = "NODE"; | 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 | class DefaultOrder { | 255 | class DefaultOrder { |
70 | public static final String CREATE_TIME = "create_time"; | 256 | public static final String CREATE_TIME = "create_time"; |
71 | } | 257 | } |
@@ -119,6 +305,7 @@ public interface FastIotConstants { | @@ -119,6 +305,7 @@ public interface FastIotConstants { | ||
119 | class MagicNumber { | 305 | class MagicNumber { |
120 | public static final int ZERO = 0; | 306 | public static final int ZERO = 0; |
121 | public static final int ONE = 1; | 307 | public static final int ONE = 1; |
308 | + public static final int TWO = 2; | ||
122 | public static final int TEN = 10; | 309 | public static final int TEN = 10; |
123 | } | 310 | } |
124 | 311 | ||
@@ -219,4 +406,22 @@ public interface FastIotConstants { | @@ -219,4 +406,22 @@ public interface FastIotConstants { | ||
219 | public static String TK_MSG_EVENT_NODE = "org.thingsboard.rule.engine.yunteng.event.TkMsgEventNode"; | 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,6 +133,15 @@ public final class ModelConstants { | ||
133 | 133 | ||
134 | /** 产品品类表 */ | 134 | /** 产品品类表 */ |
135 | public static final String TK_DEVICE_PROFILE_CATEGORY = "tk_device_profile_category"; | 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 | public static class TableFields { | 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,6 +120,22 @@ public enum ErrorMessage { | ||
120 | EZVIZ_API_ERROR(400095,"荧石视频获取TokenAPI调用失败【%s】,错误码【%s】"), | 120 | EZVIZ_API_ERROR(400095,"荧石视频获取TokenAPI调用失败【%s】,错误码【%s】"), |
121 | EZVIZ_GET_URL_ERROR(400096,"荧石API调用获取URL失败!!"), | 121 | EZVIZ_GET_URL_ERROR(400096,"荧石API调用获取URL失败!!"), |
122 | IMPORT_TCP_ERROR(400097,"TCP产品不能导入INT,DOUBLE,BOOL,TEXT以外的数据类型属性!!"), | 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 | HAVE_NO_PERMISSION(500002,"没有修改权限"), | 139 | HAVE_NO_PERMISSION(500002,"没有修改权限"), |
124 | NOT_ALLOED_ISOLATED_IN_MONOLITH(500003,"【monolith】模式下,不能选择【isolated】类型的租户配置"); | 140 | NOT_ALLOED_ISOLATED_IN_MONOLITH(500003,"【monolith】模式下,不能选择【isolated】类型的租户配置"); |
125 | 141 |
@@ -128,4 +128,10 @@ public class DeviceDTO extends TenantDTO { | @@ -128,4 +128,10 @@ public class DeviceDTO extends TenantDTO { | ||
128 | 128 | ||
129 | @ApiModelProperty(value = "是否收藏:0否1是") | 129 | @ApiModelProperty(value = "是否收藏:0否1是") |
130 | private Integer isCollect; | 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,6 +88,12 @@ public class DeviceProfileDTO extends BaseDTO { | ||
88 | @ApiModelProperty(value = "品类名称") | 88 | @ApiModelProperty(value = "品类名称") |
89 | private String categoryName; | 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 | public DeviceProfileDTO() {} | 97 | public DeviceProfileDTO() {} |
92 | 98 | ||
93 | public DeviceProfileDTO( | 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,6 +2,7 @@ package org.thingsboard.server.common.data.yunteng.dto; | ||
2 | 2 | ||
3 | import io.swagger.annotations.ApiModelProperty; | 3 | import io.swagger.annotations.ApiModelProperty; |
4 | import lombok.Data; | 4 | import lombok.Data; |
5 | +import org.thingsboard.server.common.data.yunteng.enums.HexByteOrderEnum; | ||
5 | import org.thingsboard.server.common.data.yunteng.enums.TkModBusCheckType; | 6 | import org.thingsboard.server.common.data.yunteng.enums.TkModBusCheckType; |
6 | import java.io.Serializable; | 7 | import java.io.Serializable; |
7 | import java.util.List; | 8 | import java.util.List; |
@@ -28,4 +29,7 @@ public class TkDeviceRpcDTO implements Serializable { | @@ -28,4 +29,7 @@ public class TkDeviceRpcDTO implements Serializable { | ||
28 | 29 | ||
29 | @ApiModelProperty(value = "寄存器值") | 30 | @ApiModelProperty(value = "寄存器值") |
30 | private List<Integer> registerValues; | 31 | private List<Integer> registerValues; |
32 | + | ||
33 | + @ApiModelProperty(value = "顺序") | ||
34 | + private HexByteOrderEnum hexByteOrderEnum; | ||
31 | } | 35 | } |
@@ -32,6 +32,7 @@ public class TkThingsModel implements Serializable { | @@ -32,6 +32,7 @@ public class TkThingsModel implements Serializable { | ||
32 | 32 | ||
33 | private JsonNode functionJson; | 33 | private JsonNode functionJson; |
34 | 34 | ||
35 | + private JsonNode extensionDesc; | ||
35 | 36 | ||
36 | private Integer status; | 37 | private Integer status; |
37 | 38 |
@@ -26,7 +26,6 @@ public class TkVideoDTO extends TenantDTO { | @@ -26,7 +26,6 @@ public class TkVideoDTO extends TenantDTO { | ||
26 | private String deviceType; | 26 | private String deviceType; |
27 | 27 | ||
28 | @ApiModelProperty(value = "摄像头编号/监控点位编号", required = true) | 28 | @ApiModelProperty(value = "摄像头编号/监控点位编号", required = true) |
29 | - @NotEmpty(message = "摄像头编号不能为空或空字符串") | ||
30 | private String sn; | 29 | private String sn; |
31 | 30 | ||
32 | @ApiModelProperty(value = "组织ID", required = true) | 31 | @ApiModelProperty(value = "组织ID", required = true) |
@@ -42,7 +41,7 @@ public class TkVideoDTO extends TenantDTO { | @@ -42,7 +41,7 @@ public class TkVideoDTO extends TenantDTO { | ||
42 | @ApiModelProperty(value = "摄像头描述") | 41 | @ApiModelProperty(value = "摄像头描述") |
43 | private String description; | 42 | private String description; |
44 | 43 | ||
45 | - @ApiModelProperty(value = "流获取方式:0 手动填写 1平台获取", required = true) | 44 | + @ApiModelProperty(value = "流获取方式:0 手动填写 1平台获取 2 GBT28281", required = true) |
46 | private Integer accessMode; | 45 | private Integer accessMode; |
47 | 46 | ||
48 | @ApiModelProperty(value = "平台ID") | 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 | +} |
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 | +} |
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 | +} |