What's new?
1. Wayline management: added `pause wayline task` and `recover wayline task`.
2. Added command flight function.
3. Fixed some issues.
65 files modified
2 files renamed
66 files added
1 files deleted
| | |
| | | "apikey": [ |
| | | { |
| | | "key": "value", |
| | | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2NzAzMTU2MDEsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2NzA0MDIwMDEsImlhdCI6MTY3MDMxNTYwMSwidXNlcm5hbWUiOiJhZG1pblBDIn0.yh8SkHZVsoIXo_vtlTGNB-ZX92XayalGe_q7mNRVcdI", |
| | | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2ODIyMzI5MDYsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE3Njg2MzI5MDYsImlhdCI6MTY4MjIzMjkwNiwidXNlcm5hbWUiOiJhZG1pblBDIn0.ilO-3PcvWAX9r8z3AR4VAw3kVhavYjiTx_187ACBc1M", |
| | | "type": "string" |
| | | }, |
| | | { |
| | |
| | | "apikey": [ |
| | | { |
| | | "key": "value", |
| | | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2Njg2NTk4MjYsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2Njg3NDYyMjYsImlhdCI6MTY2ODY1OTgyNiwidXNlcm5hbWUiOiJhZG1pblBDIn0.ykCpfJcReeb3etUzmNMQk1n0vaoDT6dl47J_aHRoTbU", |
| | | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2ODAyNjAxNTYsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE3NjY2NjAxNTYsImlhdCI6MTY4MDI2MDE1NiwidXNlcm5hbWUiOiJhZG1pblBDIn0._QhvfhBxxfQN7xpFqZma1rCYbBtouo2pErtm8737L_8", |
| | | "type": "string" |
| | | }, |
| | | { |
| | |
| | | "header": [], |
| | | "body": { |
| | | "mode": "raw", |
| | | "raw": "{\r\n \"name\": \"\",\r\n \"file_id\": \"\",\r\n \"dock_sn\": \"\",\r\n \"wayline_type\": 0,\r\n \"task_type\": 0,\r\n \"execute_time\": 123456789123,\r\n \"rth_altitude\": 20,\r\n \"out_of_control_action\": 1\r\n}", |
| | | "raw": "{\r\n \"name\": \"\",\r\n \"file_id\": \"\",\r\n \"dock_sn\": \"\",\r\n \"wayline_type\": 0,\r\n \"task_type\": 0,\r\n \"task_days\": [1676029468],\r\n \"task_periods\": [[1676029468]],\r\n \"rth_altitude\": 20,\r\n \"out_of_control_action\": 1\r\n}", |
| | | "options": { |
| | | "raw": { |
| | | "language": "json" |
| | |
| | | } |
| | | }, |
| | | "response": [] |
| | | }, |
| | | { |
| | | "name": "Pause Job", |
| | | "request": { |
| | | "method": "PUT", |
| | | "header": [], |
| | | "body": { |
| | | "mode": "raw", |
| | | "raw": "{\r\n \"status\": 0\r\n}", |
| | | "options": { |
| | | "raw": { |
| | | "language": "json" |
| | | } |
| | | } |
| | | }, |
| | | "url": { |
| | | "raw": "{{base_url}}{{wayline_version}}/workspaces/{{workspace_id}}/jobs/{{job_id}}", |
| | | "host": [ |
| | | "{{base_url}}{{wayline_version}}" |
| | | ], |
| | | "path": [ |
| | | "workspaces", |
| | | "{{workspace_id}}", |
| | | "jobs", |
| | | "{{job_id}}" |
| | | ] |
| | | } |
| | | }, |
| | | "response": [] |
| | | }, |
| | | { |
| | | "name": "Resume Job", |
| | | "request": { |
| | | "method": "PUT", |
| | | "header": [], |
| | | "body": { |
| | | "mode": "raw", |
| | | "raw": "{\r\n \"status\": 1\r\n}", |
| | | "options": { |
| | | "raw": { |
| | | "language": "json" |
| | | } |
| | | } |
| | | }, |
| | | "url": { |
| | | "raw": "{{base_url}}{{wayline_version}}/workspaces/{{workspace_id}}/jobs/{{job_id}}", |
| | | "host": [ |
| | | "{{base_url}}{{wayline_version}}" |
| | | ], |
| | | "path": [ |
| | | "workspaces", |
| | | "{{workspace_id}}", |
| | | "jobs", |
| | | "{{job_id}}" |
| | | ] |
| | | } |
| | | }, |
| | | "response": [] |
| | | } |
| | | ], |
| | | "auth": { |
| | |
| | | "apikey": [ |
| | | { |
| | | "key": "value", |
| | | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2Njg2NTk4MjYsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2Njg3NDYyMjYsImlhdCI6MTY2ODY1OTgyNiwidXNlcm5hbWUiOiJhZG1pblBDIn0.ykCpfJcReeb3etUzmNMQk1n0vaoDT6dl47J_aHRoTbU", |
| | | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2Nzg4NjM0NzMsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE3NjUyNjM0NzMsImlhdCI6MTY3ODg2MzQ3MywidXNlcm5hbWUiOiJhZG1pblBDIn0.r3ODgJtAHxrBCzDnCwTDCdUq8hLyfIUiDYzasYAIUII", |
| | | "type": "string" |
| | | }, |
| | | { |
| | |
| | | "header": [], |
| | | "body": { |
| | | "mode": "raw", |
| | | "raw": "{\r\n \"action\": 0\r\n}", |
| | | "raw": "", |
| | | "options": { |
| | | "raw": { |
| | | "language": "json" |
| | |
| | | } |
| | | }, |
| | | "url": { |
| | | "raw": "{{base_url}}{{control_version}}/devices/{{device_sn}}/jobs/alarm_state_switch", |
| | | "raw": "{{base_url}}{{control_version}}/devices/{{device_sn}}/jobs/return_home", |
| | | "host": [ |
| | | "{{base_url}}{{control_version}}" |
| | | ], |
| | |
| | | "devices", |
| | | "{{device_sn}}", |
| | | "jobs", |
| | | "alarm_state_switch" |
| | | "return_home" |
| | | ] |
| | | } |
| | | }, |
| | | "response": [] |
| | | }, |
| | | { |
| | | "name": "Web Drc Connect", |
| | | "request": { |
| | | "method": "POST", |
| | | "header": [], |
| | | "body": { |
| | | "mode": "raw", |
| | | "raw": "{\r\n \"client_id\": \"xxx\",\r\n \"expire_sec\": 1800\r\n}", |
| | | "options": { |
| | | "raw": { |
| | | "language": "json" |
| | | } |
| | | } |
| | | }, |
| | | "url": { |
| | | "raw": "{{base_url}}{{control_version}}/workspaces/{{workspace_id}}/drc/connect", |
| | | "host": [ |
| | | "{{base_url}}{{control_version}}" |
| | | ], |
| | | "path": [ |
| | | "workspaces", |
| | | "{{workspace_id}}", |
| | | "drc", |
| | | "connect" |
| | | ] |
| | | } |
| | | }, |
| | | "response": [] |
| | | }, |
| | | { |
| | | "name": "Enter Drc Mode", |
| | | "request": { |
| | | "method": "POST", |
| | | "header": [], |
| | | "body": { |
| | | "mode": "raw", |
| | | "raw": "{\r\n \"client_id\": \"\",\r\n \"dock_sn\": \"\"\r\n}", |
| | | "options": { |
| | | "raw": { |
| | | "language": "json" |
| | | } |
| | | } |
| | | }, |
| | | "url": { |
| | | "raw": "{{base_url}}{{control_version}}/workspaces/{{workspace_id}}/drc/enter", |
| | | "host": [ |
| | | "{{base_url}}{{control_version}}" |
| | | ], |
| | | "path": [ |
| | | "workspaces", |
| | | "{{workspace_id}}", |
| | | "drc", |
| | | "enter" |
| | | ] |
| | | } |
| | | }, |
| | | "response": [] |
| | | }, |
| | | { |
| | | "name": "Drc Mode Exit", |
| | | "request": { |
| | | "method": "POST", |
| | | "header": [], |
| | | "body": { |
| | | "mode": "raw", |
| | | "raw": "{\r\n \"client_id\": \"\",\r\n \"dock_sn\": \"\"\r\n}", |
| | | "options": { |
| | | "raw": { |
| | | "language": "json" |
| | | } |
| | | } |
| | | }, |
| | | "url": { |
| | | "raw": "{{base_url}}{{control_version}}/workspaces/{{workspace_id}}/drc/exit", |
| | | "host": [ |
| | | "{{base_url}}{{control_version}}" |
| | | ], |
| | | "path": [ |
| | | "workspaces", |
| | | "{{workspace_id}}", |
| | | "drc", |
| | | "exit" |
| | | ] |
| | | } |
| | | }, |
| | | "response": [] |
| | | }, |
| | | { |
| | | "name": "Fly to Point", |
| | | "request": { |
| | | "method": "POST", |
| | | "header": [], |
| | | "body": { |
| | | "mode": "raw", |
| | | "raw": "{\r\n \"max_speed\": 15,\r\n \"points\":[\r\n {\r\n \"latitude\": 22.5818,\r\n \"longitude\": 113.9394,\r\n \"height\": 20\r\n }\r\n ]\r\n}", |
| | | "options": { |
| | | "raw": { |
| | | "language": "json" |
| | | } |
| | | } |
| | | }, |
| | | "url": { |
| | | "raw": "{{base_url}}{{control_version}}/devices/{{device_sn}}/jobs/fly-to-point", |
| | | "host": [ |
| | | "{{base_url}}{{control_version}}" |
| | | ], |
| | | "path": [ |
| | | "devices", |
| | | "{{device_sn}}", |
| | | "jobs", |
| | | "fly-to-point" |
| | | ] |
| | | } |
| | | }, |
| | | "response": [] |
| | | }, |
| | | { |
| | | "name": "Stop Flying to Point", |
| | | "request": { |
| | | "method": "DELETE", |
| | | "header": [], |
| | | "url": { |
| | | "raw": "{{base_url}}{{control_version}}/devices/{{device_sn}}/jobs/fly-to-point", |
| | | "host": [ |
| | | "{{base_url}}{{control_version}}" |
| | | ], |
| | | "path": [ |
| | | "devices", |
| | | "{{device_sn}}", |
| | | "jobs", |
| | | "fly-to-point" |
| | | ] |
| | | } |
| | | }, |
| | | "response": [] |
| | | }, |
| | | { |
| | | "name": "Take off to Point", |
| | | "request": { |
| | | "method": "POST", |
| | | "header": [], |
| | | "body": { |
| | | "mode": "raw", |
| | | "raw": "{\r\n \"target_latitude\": 22.579,\r\n \"target_longitude\": 113.9392,\r\n \"target_height\": 20,\r\n \"security_takeoff_height\": 20,\r\n \"rth_altitude\": 20,\r\n \"rc_lost_action\": 0,\r\n \"exit_wayline_when_rc_lost\": 0,\r\n \"max_speed\": 12\r\n}", |
| | | "options": { |
| | | "raw": { |
| | | "language": "json" |
| | | } |
| | | } |
| | | }, |
| | | "url": { |
| | | "raw": "{{base_url}}{{control_version}}/devices/{{device_sn}}/jobs/takeoff-to-point", |
| | | "host": [ |
| | | "{{base_url}}{{control_version}}" |
| | | ], |
| | | "path": [ |
| | | "devices", |
| | | "{{device_sn}}", |
| | | "jobs", |
| | | "takeoff-to-point" |
| | | ] |
| | | } |
| | | }, |
| | | "response": [] |
| | | }, |
| | | { |
| | | "name": "Payload Commands", |
| | | "request": { |
| | | "method": "POST", |
| | | "header": [], |
| | | "body": { |
| | | "mode": "raw", |
| | | "raw": "{\r\n \"cmd\":\"camera_mode_switch\",\r\n \"data\":{\r\n \"payload_index\":\"53-0-0\",\r\n \"camera_mode\": 1\r\n }\r\n}", |
| | | "options": { |
| | | "raw": { |
| | | "language": "json" |
| | | } |
| | | } |
| | | }, |
| | | "url": { |
| | | "raw": "{{base_url}}{{control_version}}/devices/{{device_sn}}/payload/commands", |
| | | "host": [ |
| | | "{{base_url}}{{control_version}}" |
| | | ], |
| | | "path": [ |
| | | "devices", |
| | | "{{device_sn}}", |
| | | "payload", |
| | | "commands" |
| | | ] |
| | | } |
| | | }, |
| | | "response": [] |
| | | }, |
| | | { |
| | | "name": "Flight Authority Grab", |
| | | "request": { |
| | | "method": "POST", |
| | | "header": [], |
| | | "url": { |
| | | "raw": "{{base_url}}{{control_version}}/devices/{{device_sn}}/authority/flight", |
| | | "host": [ |
| | | "{{base_url}}{{control_version}}" |
| | | ], |
| | | "path": [ |
| | | "devices", |
| | | "{{device_sn}}", |
| | | "authority", |
| | | "flight" |
| | | ] |
| | | } |
| | | }, |
| | | "response": [] |
| | | }, |
| | | { |
| | | "name": "Payload Authority Grab", |
| | | "request": { |
| | | "method": "POST", |
| | | "header": [], |
| | | "body": { |
| | | "mode": "raw", |
| | | "raw": "{\r\n \"payload_index\":\"53-0-0\"\r\n}", |
| | | "options": { |
| | | "raw": { |
| | | "language": "json" |
| | | } |
| | | } |
| | | }, |
| | | "url": { |
| | | "raw": "{{base_url}}{{control_version}}/devices/{{device_sn}}/authority/payload", |
| | | "host": [ |
| | | "{{base_url}}{{control_version}}" |
| | | ], |
| | | "path": [ |
| | | "devices", |
| | | "{{device_sn}}", |
| | | "authority", |
| | | "payload" |
| | | ] |
| | | } |
| | | }, |
| | |
| | | "apikey": [ |
| | | { |
| | | "key": "value", |
| | | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2Njk2MzMzMzQsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE2Njk3MTk3MzQsImlhdCI6MTY2OTYzMzMzNCwidXNlcm5hbWUiOiJhZG1pblBDIn0.OoIfdpyI5eL6bFm8akq8_stzClQU41YpIJkx6_kxVHU", |
| | | "value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3b3Jrc3BhY2VfaWQiOiJlM2RlYTBmNS0zN2YyLTRkNzktYWU1OC00OTBhZjMyMjgwNjkiLCJzdWIiOiJDbG91ZEFwaVNhbXBsZSIsInVzZXJfdHlwZSI6IjEiLCJuYmYiOjE2ODIyMzI5MDYsImxvZyI6IkxvZ2dlcltjb20uZGppLnNhbXBsZS5jb21tb24ubW9kZWwuQ3VzdG9tQ2xhaW1dIiwiaXNzIjoiREpJIiwiaWQiOiJhMTU1OWU3Yy04ZGQ4LTQ3ODAtYjk1Mi0xMDBjYzQ3OTdkYTIiLCJleHAiOjE3Njg2MzI5MDYsImlhdCI6MTY4MjIzMjkwNiwidXNlcm5hbWUiOiJhZG1pblBDIn0.ilO-3PcvWAX9r8z3AR4VAw3kVhavYjiTx_187ACBc1M", |
| | | "type": "string" |
| | | }, |
| | | { |
| | |
| | | |
| | | <groupId>com.dji</groupId> |
| | | <artifactId>cloud-api-sample</artifactId> |
| | | <version>1.3.1</version> |
| | | <version>1.4.0</version> |
| | | <name>cloud-api-sample</name> |
| | | |
| | | <properties> |
| | |
| | | `device_sn` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'dock, drone, remote control', |
| | | `device_name` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'undefined' COMMENT 'model of the device. This parameter corresponds to the device name in the device dictionary table.', |
| | | `user_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT 'The account used when the device was bound.', |
| | | `nickname` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT 'custom name of the device', |
| | | `nickname` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'custom name of the device', |
| | | `workspace_id` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT '' COMMENT 'The workspace to which the current device belongs.', |
| | | `device_type` int NOT NULL DEFAULT '-1' COMMENT 'This parameter corresponds to the device type in the device dictionary table.', |
| | | `sub_type` int NOT NULL DEFAULT '-1' COMMENT 'This parameter corresponds to the sub type in the device dictionary table.', |
| | |
| | | `payload_index` smallint NOT NULL COMMENT 'The location of the payload on the device.', |
| | | `device_sn` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'Which device the current payload belongs to.', |
| | | `payload_desc` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, |
| | | `control_source` varchar(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, |
| | | `create_time` bigint NOT NULL, |
| | | `update_time` bigint NOT NULL, |
| | | PRIMARY KEY (`id`), |
| | |
| | | `completed_time` bigint DEFAULT NULL COMMENT 'actual end time', |
| | | `username` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT 'The name of the creator.', |
| | | `begin_time` bigint NOT NULL COMMENT 'planned begin time', |
| | | `end_time` bigint NOT NULL COMMENT 'planned end time', |
| | | `end_time` bigint DEFAULT NULL COMMENT 'planned end time', |
| | | `error_code` int DEFAULT NULL, |
| | | `status` int NOT NULL COMMENT '1: pending; 2: in progress; 3: success; 4: cancel; 5: failed', |
| | | `rth_altitude` int NOT NULL COMMENT 'return to home altitude. min: 20m; max: 500m', |
| | |
| | | @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class}) |
| | | public ResponseResult methodArgumentNotValidExceptionHandler(BindException e) { |
| | | e.printStackTrace(); |
| | | return ResponseResult.error(e.getBindingResult().getAllErrors().get(0).getDefaultMessage()); |
| | | return ResponseResult.error(e.getFieldError().getField() + e.getFieldError().getDefaultMessage()); |
| | | } |
| | | |
| | | } |
| | |
| | | import com.dji.sample.manage.model.enums.DeviceDomainEnum; |
| | | import com.dji.sample.manage.service.IDeviceService; |
| | | import com.dji.sample.wayline.service.IWaylineJobService; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | |
| | | |
| | | @Autowired |
| | | private IWaylineJobService waylineJobService; |
| | | |
| | | @Autowired |
| | | private ObjectMapper mapper; |
| | | /** |
| | | * Check the status of the devices every 30 seconds. It is recommended to use cache. |
| | | */ |
| | | @Scheduled(initialDelay = 30, fixedRate = 30, timeUnit = TimeUnit.SECONDS) |
| | | @Scheduled(initialDelay = 10, fixedRate = 30, timeUnit = TimeUnit.SECONDS) |
| | | private void deviceStatusListen() { |
| | | int start = RedisConst.DEVICE_ONLINE_PREFIX.length(); |
| | | |
| | |
| | | } else { |
| | | deviceService.unsubscribeTopicOffline(key.substring(start)); |
| | | deviceService.pushDeviceOfflineTopo(device.getWorkspaceId(), device.getDeviceSn()); |
| | | RedisOpsUtils.hashDel(RedisConst.LIVE_CAPACITY, new String[]{key}); |
| | | RedisOpsUtils.del(RedisConst.HMS_PREFIX + key); |
| | | RedisOpsUtils.hashDel(RedisConst.LIVE_CAPACITY, new String[]{device.getDeviceSn()}); |
| | | RedisOpsUtils.del(RedisConst.HMS_PREFIX + device.getDeviceSn()); |
| | | RedisOpsUtils.del(RedisConst.OSD_PREFIX + device.getDeviceSn()); |
| | | } |
| | | RedisOpsUtils.del(key); |
| | | } |
| | |
| | | import com.dji.sample.component.mqtt.model.MqttClientOptions; |
| | | import com.dji.sample.component.mqtt.model.MqttProtocolEnum; |
| | | import com.dji.sample.component.mqtt.model.MqttUseEnum; |
| | | import com.dji.sample.control.model.dto.MqttBrokerDTO; |
| | | import lombok.Data; |
| | | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
| | | import org.springframework.boot.context.properties.ConfigurationProperties; |
| | |
| | | return addr.toString(); |
| | | } |
| | | |
| | | /** |
| | | * Get the connection parameters of the mqtt client of the drc link. |
| | | * @param clientId |
| | | * @param username |
| | | * @param age The validity period of the token. unit: s |
| | | * @param map Custom data added in token. |
| | | * @return |
| | | */ |
| | | public static MqttBrokerDTO getMqttBrokerWithDrc(String clientId, String username, Long age, Map<String, ?> map) { |
| | | if (!mqtt.containsKey(MqttUseEnum.DRC)) { |
| | | throw new RuntimeException("Please configure the drc link parameters of mqtt in the backend configuration file first."); |
| | | } |
| | | Algorithm algorithm = JwtUtil.algorithm; |
| | | |
| | | String token = JwtUtil.createToken(map, age, algorithm, null, null); |
| | | |
| | | return MqttBrokerDTO.builder() |
| | | .address(getMqttAddress(mqtt.get(MqttUseEnum.DRC))) |
| | | .username(username) |
| | | .clientId(clientId) |
| | | .expireTime(System.currentTimeMillis() / 1000 + age) |
| | | .password(token) |
| | | .build(); |
| | | } |
| | | |
| | | |
| | | @Bean |
| | | public MqttConnectOptions mqttConnectOptions() { |
| | | MqttClientOptions customizeOptions = getBasicClientOptions(); |
| | |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.OUTBOUND_EVENTS) |
| | | public MessageChannel eventsOutboundChannel() { |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.INBOUND_EVENTS_FLIGHT_TASK_PROGRESS) |
| | | public MessageChannel eventsFlightTaskProgressChannel() { |
| | | return new DirectChannel(); |
| | |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.INBOUND_EVENTS_FLIGHT_TASK_READY) |
| | | public MessageChannel eventsEventsFlightTaskReady() { |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.INBOUND_EVENTS_FLY_TO_POINT_PROGRESS) |
| | | public MessageChannel eventsFlyToPointProgress() { |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.INBOUND_EVENTS_TAKE_OFF_TO_POINT_PROGRESS) |
| | | public MessageChannel eventsTakeoffToPointProgress() { |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.INBOUND_EVENTS_DRC_STATUS_NOTIFY) |
| | | public MessageChannel eventsDrcStatusNotify() { |
| | | return new DirectChannel(); |
| | | } |
| | | |
| | | @Bean(name = ChannelName.INBOUND_EVENTS_DRC_MODE_EXIT_NOTIFY) |
| | | public MessageChannel eventsDrcModeExitNotify() { |
| | | return new DirectChannel(); |
| | | } |
| | | } |
| | |
| | | package com.dji.sample.component.mqtt.handler; |
| | | |
| | | import com.dji.sample.component.mqtt.model.ChannelName; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicReceiver; |
| | | import com.dji.sample.component.mqtt.model.EventsMethodEnum; |
| | | import com.dji.sample.component.mqtt.model.*; |
| | | import com.dji.sample.component.mqtt.service.IMessageSenderService; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.context.annotation.Bean; |
| | | import org.springframework.context.annotation.Configuration; |
| | | import org.springframework.integration.annotation.ServiceActivator; |
| | | import org.springframework.integration.dsl.IntegrationFlow; |
| | | import org.springframework.integration.dsl.IntegrationFlows; |
| | | import org.springframework.integration.mqtt.support.MqttHeaders; |
| | | import org.springframework.messaging.MessageHeaders; |
| | | |
| | | import java.io.IOException; |
| | | import java.util.Arrays; |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean |
| | |
| | | |
| | | @Autowired |
| | | private ObjectMapper mapper; |
| | | |
| | | @Autowired |
| | | private IMessageSenderService messageSenderService; |
| | | |
| | | @Bean |
| | | public IntegrationFlow eventsMethodRouterFlow() { |
| | |
| | | methodEnum -> mapping.channelMapping(methodEnum, methodEnum.getChannelName()))) |
| | | .get(); |
| | | } |
| | | |
| | | @ServiceActivator(inputChannel = ChannelName.OUTBOUND_EVENTS, outputChannel = ChannelName.OUTBOUND) |
| | | public void replyEventsOutbound(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | if (Optional.ofNullable(receiver).map(CommonTopicReceiver::getNeedReply).flatMap(val -> Optional.of(1 != val)).orElse(true)) { |
| | | return; |
| | | } |
| | | messageSenderService.publish(headers.get(MqttHeaders.RECEIVED_TOPIC) + TopicConst._REPLY_SUF, |
| | | CommonTopicResponse.builder() |
| | | .tid(receiver.getTid()) |
| | | .bid(receiver.getBid()) |
| | | .method(receiver.getMethod()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data(RequestsReply.success()) |
| | | .build()); |
| | | |
| | | } |
| | | |
| | | } |
| | |
| | | byte[] payload = (byte[])message.getPayload(); |
| | | |
| | | CommonTopicReceiver receiver = mapper.readValue(payload, new TypeReference<CommonTopicReceiver>() {}); |
| | | ServiceReply reply; |
| | | if (LogsFileMethodEnum.FILE_UPLOAD_LIST.getMethod().equals(receiver.getMethod())) { |
| | | LogsFileUploadList list = mapper.convertValue(receiver.getData(), new TypeReference<LogsFileUploadList>() {}); |
| | | receiver.setData(list); |
| | | reply = new ServiceReply(); |
| | | reply.setResult(list.getResult()); |
| | | reply.setOutput(list.getFiles()); |
| | | } else { |
| | | ServiceReply reply = mapper.convertValue(receiver.getData(), new TypeReference<ServiceReply>() {}); |
| | | receiver.setData(reply); |
| | | reply = mapper.convertValue(receiver.getData(), new TypeReference<ServiceReply>() {}); |
| | | } |
| | | receiver.setData(reply); |
| | | Chan<CommonTopicReceiver<?>> chan = Chan.getInstance(); |
| | | // Put the message to the chan object. |
| | | chan.put(receiver); |
| | |
| | | |
| | | public static final String INBOUND_EVENTS = "inboundEvents"; |
| | | |
| | | public static final String OUTBOUND_EVENTS = "outboundEvents"; |
| | | |
| | | public static final String INBOUND_EVENTS_FLIGHT_TASK_PROGRESS = "inboundEventsFlightTaskProgress"; |
| | | |
| | | public static final String INBOUND_EVENTS_FILE_UPLOAD_CALLBACK = "inboundEventsFileUploadCallback"; |
| | |
| | | public static final String INBOUND_REQUESTS_CONFIG = "inboundRequestsConfig"; |
| | | |
| | | public static final String INBOUND_EVENTS_HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA = "inboundEventsHighestPriorityUploadFlightTaskMedia"; |
| | | |
| | | public static final String INBOUND_EVENTS_FLIGHT_TASK_READY = "inboundEventsFlightTaskReady"; |
| | | |
| | | public static final String INBOUND_EVENTS_FLY_TO_POINT_PROGRESS = "inboundFlyToPointProgress"; |
| | | |
| | | public static final String INBOUND_EVENTS_TAKE_OFF_TO_POINT_PROGRESS = "inboundTakeoffToPointProgress"; |
| | | |
| | | public static final String INBOUND_EVENTS_DRC_STATUS_NOTIFY = "inboundDrcStatusNotify"; |
| | | |
| | | public static final String INBOUND_EVENTS_DRC_MODE_EXIT_NOTIFY = "inboundDrcModeExitNotify"; |
| | | } |
| | |
| | | |
| | | HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA("highest_priority_upload_flighttask_media", ChannelName.INBOUND_EVENTS_HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA), |
| | | |
| | | FLIGHT_TASK_READY("flighttask_ready", ChannelName.INBOUND_EVENTS_FLIGHT_TASK_READY), |
| | | |
| | | FLY_TO_POINT_PROGRESS("fly_to_point_progress", ChannelName.INBOUND_EVENTS_FLY_TO_POINT_PROGRESS), |
| | | |
| | | TAKE_OFF_TO_POINT_PROGRESS("takeoff_to_point_progress", ChannelName.INBOUND_EVENTS_TAKE_OFF_TO_POINT_PROGRESS), |
| | | |
| | | DRC_STATUS_NOTIFY("drc_status_notify", ChannelName.INBOUND_EVENTS_DRC_STATUS_NOTIFY), |
| | | |
| | | JOYSTICK_INVALID_NOTIFY("joystick_invalid_notify", ChannelName.INBOUND_EVENTS_DRC_MODE_EXIT_NOTIFY), |
| | | |
| | | UNKNOWN("Unknown", ChannelName.DEFAULT); |
| | | |
| | | private String method; |
| File was renamed from src/main/java/com/dji/sample/component/mqtt/model/EventsOutputReceiver.java |
| | |
| | | * @date 2022/7/29 |
| | | */ |
| | | @Data |
| | | public class EventsOutputReceiver { |
| | | public class EventsOutputProgressReceiver<T> { |
| | | |
| | | private String status; |
| | | |
| | | private OutputProgressReceiver progress; |
| | | |
| | | private T ext; |
| | | } |
| | |
| | | |
| | | public static final String REGEX_SN = "[A-Za-z0-9]+"; |
| | | |
| | | public static final String DRC = "/drc"; |
| | | |
| | | public static final String UP = "/up"; |
| | | |
| | | public static final String DOWN = "/down"; |
| | | } |
| | |
| | | |
| | | import com.dji.sample.component.mqtt.model.CommonTopicResponse; |
| | | import com.dji.sample.component.mqtt.model.ServiceReply; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | |
| | | void publish(String topic, int qos, CommonTopicResponse response); |
| | | |
| | | /** |
| | | * Send live streaming start message and receive a response at the same time. |
| | | * Send message and receive a response at the same time. |
| | | * @param clazz |
| | | * @param topic |
| | | * @param response notification of whether the start is successful. |
| | | * @return |
| | | */ |
| | | ServiceReply publishWithReply(String topic, CommonTopicResponse response); |
| | | <T> T publishWithReply(Class<T> clazz, String topic, CommonTopicResponse response); |
| | | |
| | | /** |
| | | * Send live streaming start message and receive a response at the same time. |
| | | * Send message and receive a response at the same time. |
| | | * @param clazz |
| | | * @param topic |
| | | * @param response |
| | |
| | | * @return |
| | | */ |
| | | <T> T publishWithReply(Class<T> clazz, String topic, CommonTopicResponse response, int retryTime); |
| | | |
| | | /** |
| | | * Used exclusively for sending messages for services. |
| | | * @param clazz The generic class for ServiceReply. |
| | | * @param sn |
| | | * @param method |
| | | * @param data |
| | | * @param bid |
| | | * @param <T> |
| | | * @return |
| | | */ |
| | | <T> ServiceReply<T> publishServicesTopic(TypeReference<T> clazz, String sn, String method, Object data, String bid); |
| | | |
| | | /** |
| | | * Used exclusively for sending messages for services, and does not set the received subtype. |
| | | * @param sn |
| | | * @param method |
| | | * @param data |
| | | * @param bid |
| | | * @return |
| | | */ |
| | | ServiceReply publishServicesTopic(String sn, String method, Object data, String bid); |
| | | |
| | | /** |
| | | * Used exclusively for sending messages for services. |
| | | * @param clazz The generic class for ServiceReply. |
| | | * @param sn |
| | | * @param method |
| | | * @param data |
| | | * @param <T> |
| | | * @return |
| | | */ |
| | | <T> ServiceReply<T> publishServicesTopic(TypeReference<T> clazz, String sn, String method, Object data); |
| | | |
| | | /** |
| | | * Used exclusively for sending messages for services, and does not set the received subtype. |
| | | * @param sn |
| | | * @param method |
| | | * @param data |
| | | * @return |
| | | */ |
| | | ServiceReply publishServicesTopic(String sn, String method, Object data); |
| | | } |
| | |
| | | package com.dji.sample.component.mqtt.service.impl; |
| | | |
| | | import com.dji.sample.component.mqtt.model.Chan; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicReceiver; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicResponse; |
| | | import com.dji.sample.component.mqtt.model.ServiceReply; |
| | | import com.dji.sample.component.mqtt.model.*; |
| | | import com.dji.sample.component.mqtt.service.IMessageSenderService; |
| | | import com.dji.sample.component.mqtt.service.IMqttMessageGateway; |
| | | import com.fasterxml.jackson.core.JsonProcessingException; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.TypeMismatchException; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.util.Objects; |
| | | import java.util.UUID; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | |
| | | /** |
| | |
| | | private ObjectMapper mapper; |
| | | |
| | | public void publish(String topic, CommonTopicResponse response) { |
| | | try { |
| | | log.info("send topic: {}, payload: {}", topic, response.toString()); |
| | | messageGateway.publish(topic, mapper.writeValueAsBytes(response)); |
| | | } catch (JsonProcessingException e) { |
| | | log.info("Failed to publish the message. {}", response.toString()); |
| | | e.printStackTrace(); |
| | | } |
| | | this.publish(topic, 1, response); |
| | | } |
| | | |
| | | public void publish(String topic, int qos, CommonTopicResponse response) { |
| | | try { |
| | | log.info("send topic: {}, payload: {}", topic, response.toString()); |
| | | messageGateway.publish(topic, mapper.writeValueAsBytes(response), qos); |
| | | } catch (JsonProcessingException e) { |
| | | log.info("Failed to publish the message. {}", response.toString()); |
| | |
| | | } |
| | | } |
| | | |
| | | public ServiceReply publishWithReply(String topic, CommonTopicResponse response) { |
| | | return this.publishWithReply(ServiceReply.class, topic, response, 2); |
| | | public <T> T publishWithReply(Class<T> clazz, String topic, CommonTopicResponse response) { |
| | | return this.publishWithReply(clazz, topic, response, 2); |
| | | } |
| | | |
| | | public <T> T publishWithReply(Class<T> clazz, String topic, CommonTopicResponse response, int retryTime) { |
| | | log.info("send topic: {}, payload: {}", topic, response.toString()); |
| | | AtomicInteger time = new AtomicInteger(0); |
| | | // Retry three times |
| | | while (time.getAndIncrement() <= retryTime) { |
| | |
| | | Chan<CommonTopicReceiver<T>> chan = Chan.getInstance(); |
| | | // If the message is not received in 0.5 seconds then resend it again. |
| | | CommonTopicReceiver<T> receiver = chan.get(response.getTid()); |
| | | if (receiver == null) { |
| | | continue; |
| | | } |
| | | |
| | | // Need to match tid and bid. |
| | | if (receiver.getTid().equals(response.getTid()) && |
| | | if (Objects.nonNull(receiver) && receiver.getTid().equals(response.getTid()) && |
| | | receiver.getBid().equals(response.getBid())) { |
| | | if (clazz.isAssignableFrom(receiver.getData().getClass())) { |
| | | return receiver.getData(); |
| | | } |
| | | throw new TypeMismatchException(receiver.getData(), clazz); |
| | | } |
| | | // It must be guaranteed that the tid and bid of each message are different. |
| | | response.setBid(UUID.randomUUID().toString()); |
| | | response.setTid(UUID.randomUUID().toString()); |
| | | } |
| | | throw new RuntimeException("No message reply received."); |
| | | } |
| | | |
| | | @Override |
| | | public <T> ServiceReply<T> publishServicesTopic(TypeReference<T> clazz, String sn, String method, Object data, String bid) { |
| | | String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + sn + TopicConst.SERVICES_SUF; |
| | | ServiceReply reply = this.publishWithReply(ServiceReply.class, topic, |
| | | CommonTopicResponse.builder() |
| | | .tid(UUID.randomUUID().toString()) |
| | | .bid(StringUtils.hasText(bid) ? bid : UUID.randomUUID().toString()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .method(method) |
| | | .data(Objects.requireNonNullElse(data, "")) |
| | | .build()); |
| | | if (Objects.isNull(clazz)) { |
| | | return reply; |
| | | } |
| | | // put together in "output" |
| | | if (Objects.nonNull(reply.getInfo())) { |
| | | reply.setOutput(mapper.convertValue(reply.getInfo(), clazz)); |
| | | } |
| | | if (Objects.nonNull(reply.getOutput())) { |
| | | reply.setOutput(mapper.convertValue(reply.getOutput(), clazz)); |
| | | } |
| | | return reply; |
| | | } |
| | | |
| | | @Override |
| | | public ServiceReply publishServicesTopic(String sn, String method, Object data, String bid) { |
| | | return this.publishServicesTopic(null, sn, method, data, bid); |
| | | } |
| | | |
| | | @Override |
| | | public <T> ServiceReply<T> publishServicesTopic(TypeReference<T> clazz, String sn, String method, Object data) { |
| | | return this.publishServicesTopic(clazz, sn, method, data, null); |
| | | } |
| | | |
| | | @Override |
| | | public ServiceReply publishServicesTopic(String sn, String method, Object data) { |
| | | return this.publishServicesTopic(null, sn, method, data, null); |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | public static final String WAYLINE_JOB_TIMED_EXECUTE = "wayline_job_timed_execute"; |
| | | |
| | | public static final String WAYLINE_JOB_CONDITION_PREPARE = "wayline_job_condition_prepare"; |
| | | |
| | | public static final String WAYLINE_JOB_CONDITION_PREFIX = WAYLINE_JOB_CONDITION_PREPARE + DELIMITER; |
| | | |
| | | public static final String WAYLINE_JOB_BLOCK_PREFIX = "wayline_job_block" + DELIMITER; |
| | | |
| | | public static final String WAYLINE_JOB_RUNNING_PREFIX = "wayline_job_running" + DELIMITER; |
| | |
| | | |
| | | public static final String LIVE_CAPACITY = "live_capacity"; |
| | | |
| | | public static final String DRC_PREFIX = "drc" + DELIMITER; |
| | | |
| | | public static final Integer DRC_MODE_ALIVE_SECOND = 3600; |
| | | |
| | | public static final String MQTT_ACL_PREFIX = "mqtt_acl" + DELIMITER; |
| | | |
| | | public static final String FILE_UPLOADING_PREFIX = "file_uploading" + DELIMITER; |
| | | |
| | | public static final String DRONE_CONTROL_PREFiX = "control_source" + DELIMITER; |
| | | } |
| | |
| | | |
| | | FILE_UPLOAD_CALLBACK("file_upload_callback"), |
| | | |
| | | HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA("HIGHEST_PRIORITY_UPLOAD_FLIGHTTASK_MEDIA"); |
| | | FILE_UPLOAD_PROGRESS("fileupload_progress"), |
| | | |
| | | OTA_PROGRESS("ota_progress"), |
| | | |
| | | HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA("highest_priority_upload_flighttask_media"), |
| | | |
| | | CONTROL_SOURCE_CHANGE("control_source_change"), |
| | | |
| | | FLY_TO_POINT_PROGRESS("fly_to_point_progress"), |
| | | |
| | | TAKE_OFF_TO_POINT_PROGRESS("takeoff_to_point_progress"), |
| | | |
| | | DRC_STATUS_NOTIFY("drc_status_notify"), |
| | | |
| | | JOYSTICK_INVALID_NOTIFY("joystick_invalid_notify") |
| | | ; |
| | | |
| | | private String code; |
| | | |
| | |
| | | * @param message message |
| | | */ |
| | | void sendBatch(Collection<ConcurrentWebSocketSession> sessions, CustomWebSocketMessage message); |
| | | |
| | | void sendBatch(String workspaceId, Integer userType, String bizCode, Object data); |
| | | |
| | | void sendBatch(String workspaceId, String bizCode, Object data); |
| | | } |
| | |
| | | import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | import com.dji.sample.component.websocket.service.ISendMessageService; |
| | | import com.dji.sample.component.websocket.service.IWebSocketManageService; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.StringUtils; |
| | | import org.springframework.web.socket.TextMessage; |
| | | |
| | | import java.io.IOException; |
| | | import java.util.Collection; |
| | | import java.util.Objects; |
| | | |
| | | /** |
| | | * @author sean.zhou |
| | |
| | | |
| | | @Autowired |
| | | private ObjectMapper mapper; |
| | | |
| | | @Autowired |
| | | private IWebSocketManageService webSocketManageService; |
| | | |
| | | @Override |
| | | public void sendMessage(ConcurrentWebSocketSession session, CustomWebSocketMessage message) { |
| | |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void sendBatch(String workspaceId, Integer userType, String bizCode, Object data) { |
| | | if (!StringUtils.hasText(workspaceId)) { |
| | | throw new RuntimeException("Workspace ID does not exist."); |
| | | } |
| | | Collection<ConcurrentWebSocketSession> sessions = Objects.isNull(userType) ? |
| | | webSocketManageService.getValueWithWorkspace(workspaceId) : |
| | | webSocketManageService.getValueWithWorkspaceAndUserType(workspaceId, userType); |
| | | |
| | | this.sendBatch(sessions, CustomWebSocketMessage.builder() |
| | | .data(data) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(bizCode) |
| | | .build()); |
| | | } |
| | | |
| | | @Override |
| | | public void sendBatch(String workspaceId, String bizCode, Object data) { |
| | | this.sendBatch(workspaceId, null, bizCode, data); |
| | | } |
| | | } |
| | |
| | | package com.dji.sample.control.controller; |
| | | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.control.model.param.RemoteDebugParam; |
| | | import com.dji.sample.control.model.enums.DroneAuthorityEnum; |
| | | import com.dji.sample.control.model.param.*; |
| | | import com.dji.sample.control.service.IControlService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.validation.Valid; |
| | | |
| | | /** |
| | | * @author sean |
| | |
| | | public ResponseResult createControlJob(@PathVariable String sn, |
| | | @PathVariable("service_identifier") String serviceIdentifier, |
| | | @RequestBody(required = false) RemoteDebugParam param) { |
| | | return controlService.controlDock(sn, serviceIdentifier, param); |
| | | return controlService.controlDockDebug(sn, serviceIdentifier, param); |
| | | } |
| | | |
| | | @PostMapping("/{sn}/jobs/fly-to-point") |
| | | public ResponseResult flyToPoint(@PathVariable String sn, @Valid @RequestBody FlyToPointParam param) { |
| | | return controlService.flyToPoint(sn, param); |
| | | } |
| | | |
| | | @DeleteMapping("/{sn}/jobs/fly-to-point") |
| | | public ResponseResult flyToPointStop(@PathVariable String sn) { |
| | | return controlService.flyToPointStop(sn); |
| | | } |
| | | |
| | | @PostMapping("/{sn}/jobs/takeoff-to-point") |
| | | public ResponseResult takeoffToPoint(@PathVariable String sn, @Valid @RequestBody TakeoffToPointParam param) { |
| | | return controlService.takeoffToPoint(sn, param); |
| | | } |
| | | |
| | | @PostMapping("/{sn}/authority/flight") |
| | | public ResponseResult seizeFlightAuthority(@PathVariable String sn) { |
| | | return controlService.seizeAuthority(sn, DroneAuthorityEnum.FLIGHT, null); |
| | | } |
| | | |
| | | @PostMapping("/{sn}/authority/payload") |
| | | public ResponseResult seizePayloadAuthority(@PathVariable String sn, @Valid @RequestBody DronePayloadParam param) { |
| | | return controlService.seizeAuthority(sn, DroneAuthorityEnum.PAYLOAD, param); |
| | | } |
| | | |
| | | @PostMapping("/{sn}/payload/commands") |
| | | public ResponseResult payloadCommands(@PathVariable String sn, @Valid @RequestBody PayloadCommandsParam param) throws Exception { |
| | | param.setSn(sn); |
| | | return controlService.payloadCommands(param); |
| | | } |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.controller; |
| | | |
| | | import com.dji.sample.common.model.CustomClaim; |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.control.model.dto.JwtAclDTO; |
| | | import com.dji.sample.control.model.dto.MqttBrokerDTO; |
| | | import com.dji.sample.control.model.param.DrcConnectParam; |
| | | import com.dji.sample.control.model.param.DrcModeParam; |
| | | import com.dji.sample.control.service.IDrcService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.servlet.http.HttpServletRequest; |
| | | import javax.validation.Valid; |
| | | |
| | | import static com.dji.sample.component.AuthInterceptor.TOKEN_CLAIM; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/1/11 |
| | | */ |
| | | @RestController |
| | | @Slf4j |
| | | @RequestMapping("${url.control.prefix}${url.control.version}") |
| | | public class DrcController { |
| | | |
| | | @Autowired |
| | | private IDrcService drcService; |
| | | |
| | | @PostMapping("/workspaces/{workspace_id}/drc/connect") |
| | | public ResponseResult drcConnect(@PathVariable("workspace_id") String workspaceId, HttpServletRequest request, @Valid @RequestBody DrcConnectParam param) { |
| | | CustomClaim claims = (CustomClaim) request.getAttribute(TOKEN_CLAIM); |
| | | |
| | | MqttBrokerDTO brokerDTO = drcService.userDrcAuth(workspaceId, claims.getId(), claims.getUsername(), param); |
| | | return ResponseResult.success(brokerDTO); |
| | | } |
| | | |
| | | @PostMapping("/workspaces/{workspace_id}/drc/enter") |
| | | public ResponseResult drcEnter(@PathVariable("workspace_id") String workspaceId, @Valid @RequestBody DrcModeParam param) { |
| | | JwtAclDTO acl = drcService.deviceDrcEnter(workspaceId, param); |
| | | |
| | | return ResponseResult.success(acl); |
| | | } |
| | | |
| | | @PostMapping("/workspaces/{workspace_id}/drc/exit") |
| | | public ResponseResult drcExit(@PathVariable("workspace_id") String workspaceId, @Valid @RequestBody DrcModeParam param) { |
| | | drcService.deviceDrcExit(workspaceId, param); |
| | | |
| | | return ResponseResult.success(); |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | package com.dji.sample.control.model.dto; |
| | | |
| | | import com.dji.sample.control.service.impl.RemoteDebugHandler; |
| | | import com.dji.sample.manage.model.enums.StateSwitchEnum; |
| | | import com.dji.sample.manage.model.receiver.BasicDeviceProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class AlarmState extends BasicDeviceProperty { |
| | | public class AlarmState extends RemoteDebugHandler { |
| | | |
| | | private Integer action; |
| | | |
| | |
| | | package com.dji.sample.control.model.dto; |
| | | |
| | | import com.dji.sample.control.model.enums.BatteryStoreModeEnum; |
| | | import com.dji.sample.manage.model.receiver.BasicDeviceProperty; |
| | | import com.dji.sample.control.service.impl.RemoteDebugHandler; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class BatteryStoreMode extends BasicDeviceProperty { |
| | | public class BatteryStoreMode extends RemoteDebugHandler { |
| | | |
| | | private Integer action; |
| | | |
| New file |
| | |
| | | package com.dji.sample.control.model.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/1/12 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class DrcModeDTO { |
| | | |
| | | private MqttBrokerDTO mqttBroker; |
| | | |
| | | /** |
| | | * range: 1 - 30 |
| | | */ |
| | | @Builder.Default |
| | | private Integer osdFrequency = 10; |
| | | |
| | | /** |
| | | * range: 1 - 30 |
| | | */ |
| | | @Builder.Default |
| | | private Integer hsiFrequency = 1; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.dto; |
| | | |
| | | import com.dji.sample.control.model.enums.DrcModeReasonEnum; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/14 |
| | | */ |
| | | @Data |
| | | public class DrcModeReasonReceiver { |
| | | |
| | | private DrcModeReasonEnum reason; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.dto; |
| | | |
| | | import com.dji.sample.control.model.enums.DrcStatusErrorEnum; |
| | | import com.dji.sample.manage.model.enums.DockDrcStateEnum; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/17 |
| | | */ |
| | | @Data |
| | | public class DrcStatusNotifyReceiver { |
| | | |
| | | private DrcStatusErrorEnum result; |
| | | |
| | | private DockDrcStateEnum drcState; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.dto; |
| | | |
| | | import com.dji.sample.control.model.enums.FlyToStatusEnum; |
| | | import com.dji.sample.wayline.model.enums.WaylineErrorCodeEnum; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/14 |
| | | */ |
| | | @Data |
| | | public class FlyToProgressReceiver { |
| | | |
| | | private WaylineErrorCodeEnum result; |
| | | |
| | | private FlyToStatusEnum status; |
| | | |
| | | private String flyToId; |
| | | |
| | | private Integer wayPointIndex; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/1/12 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class JwtAclDTO { |
| | | |
| | | private List<String> sub; |
| | | |
| | | private List<String> pub; |
| | | |
| | | private List<String> all; |
| | | } |
| | |
| | | package com.dji.sample.control.model.dto; |
| | | |
| | | import com.dji.sample.control.model.enums.LinkWorkModeEnum; |
| | | import com.dji.sample.manage.model.receiver.BasicDeviceProperty; |
| | | import com.dji.sample.control.service.impl.RemoteDebugHandler; |
| | | import com.fasterxml.jackson.annotation.JsonProperty; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class LinkWorkMode extends BasicDeviceProperty { |
| | | public class LinkWorkMode extends RemoteDebugHandler { |
| | | |
| | | @JsonProperty("link_workmode") |
| | | private Integer linkWorkMode; |
| New file |
| | |
| | | package com.dji.sample.control.model.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/1/11 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class MqttBrokerDTO { |
| | | |
| | | private String address; |
| | | |
| | | private String username; |
| | | |
| | | private String password; |
| | | |
| | | private String clientId; |
| | | |
| | | private Long expireTime; |
| | | |
| | | @Builder.Default |
| | | private Boolean enableTls = false; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.dto; |
| | | |
| | | import lombok.Data; |
| | | import org.hibernate.validator.constraints.Range; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/2/14 |
| | | */ |
| | | @Data |
| | | public class PointDTO { |
| | | |
| | | @Range(min = -90, max = 90) |
| | | @NotNull |
| | | private Double latitude; |
| | | |
| | | @NotNull |
| | | @Range(min = -180, max = 180) |
| | | private Double longitude; |
| | | |
| | | /** |
| | | * WGS84 |
| | | * The M30 series are ellipsoidal heights. |
| | | */ |
| | | @NotNull |
| | | @Range(min = 2, max = 1500) |
| | | private Double height; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.dto; |
| | | |
| | | import com.dji.sample.common.util.SpringBeanUtils; |
| | | import com.dji.sample.control.service.impl.RemoteDebugHandler; |
| | | import com.dji.sample.manage.model.enums.DockModeCodeEnum; |
| | | import com.dji.sample.manage.service.IDeviceService; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/4/14 |
| | | */ |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @Data |
| | | public class RemoteDebugOpenState extends RemoteDebugHandler { |
| | | |
| | | @Override |
| | | public boolean canPublish(String sn) { |
| | | IDeviceService deviceService = SpringBeanUtils.getBean(IDeviceService.class); |
| | | DockModeCodeEnum dockMode = deviceService.getDockMode(sn); |
| | | return DockModeCodeEnum.IDLE == dockMode; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/1 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class ResultNotifyDTO { |
| | | |
| | | private Integer result; |
| | | |
| | | private String message; |
| | | |
| | | private String sn; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.dto; |
| | | |
| | | import com.dji.sample.common.util.SpringBeanUtils; |
| | | import com.dji.sample.control.service.impl.RemoteDebugHandler; |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.model.receiver.OsdSubDeviceReceiver; |
| | | import com.dji.sample.manage.service.IDeviceRedisService; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/4/19 |
| | | */ |
| | | |
| | | public class ReturnHomeState extends RemoteDebugHandler { |
| | | |
| | | @Override |
| | | public boolean canPublish(String sn) { |
| | | IDeviceRedisService deviceRedisService = SpringBeanUtils.getBean(IDeviceRedisService.class); |
| | | return deviceRedisService.getDeviceOnline(sn) |
| | | .map(DeviceDTO::getChildDeviceSn) |
| | | .flatMap(deviceSn -> deviceRedisService.getDeviceOsd(deviceSn, OsdSubDeviceReceiver.class)) |
| | | .map(OsdSubDeviceReceiver::getElevation) |
| | | .map(elevation -> elevation > 0) |
| | | .orElse(false); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.dto; |
| | | |
| | | import com.dji.sample.control.model.enums.TakeoffStatusEnum; |
| | | import com.dji.sample.wayline.model.enums.WaylineErrorCodeEnum; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/14 |
| | | */ |
| | | @Data |
| | | public class TakeoffProgressReceiver { |
| | | |
| | | private WaylineErrorCodeEnum result; |
| | | |
| | | private TakeoffStatusEnum status; |
| | | |
| | | private String flightId; |
| | | |
| | | private String trackId; |
| | | |
| | | private Integer wayPointIndex; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.enums; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | import com.fasterxml.jackson.annotation.JsonValue; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/3 |
| | | */ |
| | | public enum CameraModeEnum { |
| | | |
| | | PHOTO, VIDEO; |
| | | |
| | | @JsonValue |
| | | public int getVal() { |
| | | return ordinal(); |
| | | } |
| | | |
| | | @JsonCreator |
| | | public static CameraModeEnum find(int val) { |
| | | return Arrays.stream(values()).filter(modeEnum -> modeEnum.ordinal() == val).findAny().get(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.enums; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | import com.fasterxml.jackson.annotation.JsonValue; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/4/23 |
| | | */ |
| | | public enum CameraStateEnum { |
| | | |
| | | IDLE, WORKING; |
| | | |
| | | @JsonValue |
| | | public int getVal() { |
| | | return ordinal(); |
| | | } |
| | | |
| | | @JsonCreator |
| | | public static CameraStateEnum find(int val) { |
| | | return Arrays.stream(values()).filter(stateEnum -> stateEnum.ordinal() == val).findAny().get(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.enums; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | import com.fasterxml.jackson.annotation.JsonValue; |
| | | import lombok.Getter; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/3 |
| | | */ |
| | | @Getter |
| | | public enum CameraTypeEnum { |
| | | |
| | | ZOOM("zoom"), |
| | | |
| | | WIDE("wide"), |
| | | |
| | | IR("ir"); |
| | | |
| | | String type; |
| | | |
| | | CameraTypeEnum(String type) { |
| | | this.type = type; |
| | | } |
| | | |
| | | @JsonValue |
| | | public String getType() { |
| | | return type; |
| | | } |
| | | |
| | | @JsonCreator |
| | | public static CameraTypeEnum find(String cameraType) { |
| | | return Arrays.stream(CameraTypeEnum.values()).filter(typeEnum -> typeEnum.type.equals(cameraType)).findAny().get(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.enums; |
| | | |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/1/11 |
| | | */ |
| | | @Getter |
| | | public enum DrcMethodEnum { |
| | | |
| | | DRC_MODE_ENTER("drc_mode_enter"), |
| | | |
| | | DRC_MODE_EXIT("drc_mode_exit"); |
| | | |
| | | String method; |
| | | |
| | | DrcMethodEnum(String method) { |
| | | this.method = method; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.enums; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/14 |
| | | */ |
| | | public enum DrcModeReasonEnum { |
| | | |
| | | UNKNOWN(-1, "unknown"), |
| | | |
| | | RC_LOST(0, "The remote controller is lost."), |
| | | |
| | | BATTERY_LOW_GO_HOME(1, "Due to low battery, the drone automatically returned home."), |
| | | |
| | | BATTERY_SUPER_LOW_LANDING(2, "Due to the serious low battery, the drone landed automatically."), |
| | | |
| | | NEAR_BOUNDARY(3, "The drone is near a not-fly zone."), |
| | | |
| | | RC_AUTHORITY(4, "The remote controller grabs control authority."); |
| | | |
| | | int val; |
| | | |
| | | String message; |
| | | |
| | | DrcModeReasonEnum(int val, String message) { |
| | | this.val = val; |
| | | this.message = message; |
| | | } |
| | | |
| | | public int getVal() { |
| | | return val; |
| | | } |
| | | |
| | | public String getMessage() { |
| | | return message; |
| | | } |
| | | |
| | | @JsonCreator(mode = JsonCreator.Mode.DELEGATING) |
| | | public static DrcModeReasonEnum find(int val) { |
| | | return Arrays.stream(values()).filter(reasonEnum -> reasonEnum.val == val).findAny().orElse(UNKNOWN); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.enums; |
| | | |
| | | import com.dji.sample.common.error.IErrorInfo; |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/17 |
| | | */ |
| | | public enum DrcStatusErrorEnum implements IErrorInfo { |
| | | |
| | | SUCCESS(0, "success"), |
| | | |
| | | MQTT_ERR(514300, "The mqtt connection error."), |
| | | |
| | | HEARTBEAT_TIMEOUT(514301, "The heartbeat times out and the dock disconnects."), |
| | | |
| | | MQTT_CERTIFICATE_ERR(514302, "The mqtt certificate is abnormal and the connection fails."), |
| | | |
| | | MQTT_LOST(514303, "The dock network is abnormal and the mqtt connection is lost."), |
| | | |
| | | MQTT_REFUSE(514304, "The dock connection to mqtt server was refused."), |
| | | |
| | | UNKNOWN(-1, "Unknown"); |
| | | |
| | | String msg; |
| | | |
| | | int code; |
| | | |
| | | DrcStatusErrorEnum(int code, String msg) { |
| | | this.code = code; |
| | | this.msg = msg; |
| | | } |
| | | |
| | | @Override |
| | | public String getErrorMsg() { |
| | | return msg; |
| | | } |
| | | |
| | | @Override |
| | | public Integer getErrorCode() { |
| | | return code; |
| | | } |
| | | |
| | | @JsonCreator(mode = JsonCreator.Mode.DELEGATING) |
| | | public static DrcStatusErrorEnum find(int code) { |
| | | return Arrays.stream(values()).filter(error -> error.code == code).findAny().orElse(UNKNOWN); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.enums; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonValue; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/1 |
| | | */ |
| | | public enum DroneAuthorityEnum { |
| | | |
| | | FLIGHT(1), PAYLOAD(2); |
| | | |
| | | int val; |
| | | |
| | | DroneAuthorityEnum(int val) { |
| | | this.val = val; |
| | | } |
| | | |
| | | @JsonValue |
| | | public int getVal() { |
| | | return val; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.enums; |
| | | |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/2/21 |
| | | */ |
| | | @Getter |
| | | public enum DroneControlMethodEnum { |
| | | |
| | | FLIGHT_AUTHORITY_GRAB("flight_authority_grab"), |
| | | |
| | | PAYLOAD_AUTHORITY_GRAB("payload_authority_grab"), |
| | | |
| | | FLY_TO_POINT("fly_to_point"), |
| | | |
| | | FLY_TO_POINT_STOP("fly_to_point_stop"), |
| | | |
| | | TAKE_OFF_TO_POINT("takeoff_to_point"); |
| | | |
| | | String method; |
| | | |
| | | DroneControlMethodEnum(String method) { |
| | | this.method = method; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.enums; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/14 |
| | | */ |
| | | public enum FlyToStatusEnum { |
| | | |
| | | WAYLINE_PROGRESS("wayline_progress", "The FlyTo job is in progress."), |
| | | |
| | | WAYLINE_FAILED("wayline_failed", "The Fly To task execution failed."), |
| | | |
| | | WAYLINE_OK("wayline_ok", "The FlyTo job executed successfully."), |
| | | |
| | | WAYLINE_CANCEL("wayline_cancel", "The FlyTo job is closed."); |
| | | |
| | | String status; |
| | | |
| | | String message; |
| | | |
| | | FlyToStatusEnum(String status, String message) { |
| | | this.status = status; |
| | | this.message = message; |
| | | } |
| | | |
| | | public String getMessage() { |
| | | return message; |
| | | } |
| | | |
| | | @JsonCreator(mode = JsonCreator.Mode.DELEGATING) |
| | | public static FlyToStatusEnum find(String status) { |
| | | return Arrays.stream(values()).filter(statusEnum -> statusEnum.status.equals(status)).findAny().get(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.enums; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | import com.fasterxml.jackson.annotation.JsonValue; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/13 |
| | | */ |
| | | public enum GimbalResetModeEnum { |
| | | |
| | | RECENTER, DOWN, RECENTER_PAN, PITCH_DOWN; |
| | | |
| | | @JsonValue |
| | | public int getVal() { |
| | | return ordinal(); |
| | | } |
| | | |
| | | @JsonCreator |
| | | public static GimbalResetModeEnum find(int value) { |
| | | return Arrays.stream(values()).filter(resetModeEnum -> resetModeEnum.ordinal() == value).findAny().get(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.enums; |
| | | |
| | | import lombok.Getter; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/1/13 |
| | | */ |
| | | @Getter |
| | | public enum MqttAclAccessEnum { |
| | | |
| | | SUB(1), |
| | | |
| | | PUB(2), |
| | | |
| | | ALL(3); |
| | | |
| | | int value; |
| | | |
| | | MqttAclAccessEnum(int value) { |
| | | this.value = value; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.enums; |
| | | |
| | | import com.dji.sample.control.service.impl.*; |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | import com.fasterxml.jackson.annotation.JsonValue; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/2 |
| | | */ |
| | | public enum PayloadCommandsEnum { |
| | | |
| | | CAMERA_MODE_SWitCH("camera_mode_switch", CameraModeSwitchImpl.class), |
| | | |
| | | CAMERA_PHOTO_TAKE("camera_photo_take", CameraPhotoTakeImpl.class), |
| | | |
| | | CAMERA_RECORDING_START("camera_recording_start", CameraRecordingStartImpl.class), |
| | | |
| | | CAMERA_RECORDING_STOP("camera_recording_stop", CameraRecordingStopImpl.class), |
| | | |
| | | CAMERA_AIM("camera_aim", CameraAimImpl.class), |
| | | |
| | | CAMERA_FOCAL_LENGTH_SET("camera_focal_length_set", CameraFocalLengthSetImpl.class), |
| | | |
| | | GIMBAL_RESET("gimbal_reset", GimbalResetImpl.class); |
| | | |
| | | String cmd; |
| | | |
| | | Class<? extends PayloadCommandsHandler> clazz; |
| | | |
| | | PayloadCommandsEnum(String cmd, Class<? extends PayloadCommandsHandler> clazz) { |
| | | this.cmd = cmd; |
| | | this.clazz = clazz; |
| | | } |
| | | |
| | | @JsonValue |
| | | public String getCmd() { |
| | | return cmd; |
| | | } |
| | | |
| | | public Class<? extends PayloadCommandsHandler> getClazz() { |
| | | return clazz; |
| | | } |
| | | |
| | | @JsonCreator(mode = JsonCreator.Mode.DELEGATING) |
| | | public static PayloadCommandsEnum find(String cmd) { |
| | | return Arrays.stream(values()).filter(cmdEnum -> cmdEnum.cmd.equals(cmd)).findAny().get(); |
| | | } |
| | | } |
| File was renamed from src/main/java/com/dji/sample/control/model/enums/RemoteControlMethodEnum.java |
| | |
| | | package com.dji.sample.control.model.enums; |
| | | |
| | | import com.dji.sample.control.model.dto.AlarmState; |
| | | import com.dji.sample.control.model.dto.BatteryStoreMode; |
| | | import com.dji.sample.control.model.dto.LinkWorkMode; |
| | | import com.dji.sample.manage.model.receiver.BasicDeviceProperty; |
| | | import com.dji.sample.control.model.dto.*; |
| | | import com.dji.sample.control.service.impl.RemoteDebugHandler; |
| | | import lombok.Getter; |
| | | |
| | | import java.util.Arrays; |
| | |
| | | * @date 2022/11/14 |
| | | */ |
| | | @Getter |
| | | public enum RemoteControlMethodEnum { |
| | | public enum RemoteDebugMethodEnum { |
| | | |
| | | DEBUG_MODE_OPEN("debug_mode_open", false, null), |
| | | DEBUG_MODE_OPEN("debug_mode_open", false, RemoteDebugOpenState.class), |
| | | |
| | | DEBUG_MODE_CLOSE("debug_mode_close", false, null), |
| | | |
| | |
| | | |
| | | SUPPLEMENT_LIGHT_CLOSE("supplement_light_close", false, null), |
| | | |
| | | RETURN_HOME("return_home", false, null), |
| | | RETURN_HOME("return_home", false, ReturnHomeState.class), |
| | | |
| | | DEVICE_REBOOT("device_reboot", true, null), |
| | | |
| | |
| | | |
| | | private Boolean progress; |
| | | |
| | | private Class<? extends BasicDeviceProperty> clazz; |
| | | private Class<? extends RemoteDebugHandler> clazz; |
| | | |
| | | RemoteControlMethodEnum(String method, Boolean progress, Class<? extends BasicDeviceProperty> clazz) { |
| | | RemoteDebugMethodEnum(String method, Boolean progress, Class<? extends RemoteDebugHandler> clazz) { |
| | | this.method = method; |
| | | this.progress = progress; |
| | | this.clazz = clazz; |
| | | } |
| | | |
| | | public static RemoteControlMethodEnum find(String method) { |
| | | return Arrays.stream(RemoteControlMethodEnum.values()) |
| | | public static RemoteDebugMethodEnum find(String method) { |
| | | return Arrays.stream(RemoteDebugMethodEnum.values()) |
| | | .filter(methodEnum -> methodEnum.method.equals(method)) |
| | | .findAny() |
| | | .orElse(UNKNOWN); |
| New file |
| | |
| | | package com.dji.sample.control.model.enums; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/17 |
| | | */ |
| | | public enum TakeoffStatusEnum { |
| | | |
| | | TASK_READY("task_ready", "The drone is preparing to take off."), |
| | | |
| | | WAYLINE_PROGRESS("wayline_progress", "The drone is taking off."), |
| | | |
| | | WAYLINE_FAILED("wayline_failed", "The drone failed to take off."), |
| | | |
| | | WAYLINE_OK("wayline_ok", "The drone took off successfully."), |
| | | |
| | | WAYLINE_CANCEL("wayline_cancel", "The drone takeoff job has been cancelled."), |
| | | |
| | | TASK_FINISH("task_finish", "The drone takeoff job is completed."); |
| | | |
| | | String status; |
| | | |
| | | String message; |
| | | |
| | | TakeoffStatusEnum(String status, String message) { |
| | | this.status = status; |
| | | this.message = message; |
| | | } |
| | | |
| | | public String getStatus() { |
| | | return status; |
| | | } |
| | | |
| | | public String getMessage() { |
| | | return message; |
| | | } |
| | | |
| | | @JsonCreator(mode = JsonCreator.Mode.DELEGATING) |
| | | public static TakeoffStatusEnum find(String status) { |
| | | return Arrays.stream(values()).filter(statusEnum -> statusEnum.status.equals(status)).findAny().get(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.param; |
| | | |
| | | import lombok.Data; |
| | | import org.hibernate.validator.constraints.Range; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/2/2 |
| | | */ |
| | | @Data |
| | | public class DeviceDrcInfoParam { |
| | | |
| | | @Range(min = 1, max = 30) |
| | | private Integer osdFrequency = 10; |
| | | |
| | | @Range(min = 1, max = 30) |
| | | private Integer hsiFrequency = 1; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.param; |
| | | |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import lombok.Data; |
| | | import org.hibernate.validator.constraints.Range; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/1/11 |
| | | */ |
| | | @Data |
| | | public class DrcConnectParam { |
| | | |
| | | private String clientId; |
| | | |
| | | @Range(min = 1800, max = 86400) |
| | | private long expireSec = RedisConst.DRC_MODE_ALIVE_SECOND; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.param; |
| | | |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | import org.hibernate.validator.constraints.Range; |
| | | |
| | | import javax.validation.Valid; |
| | | import javax.validation.constraints.NotBlank; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/1/11 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class DrcModeParam { |
| | | |
| | | @NotBlank |
| | | private String clientId; |
| | | |
| | | @NotBlank |
| | | private String dockSn; |
| | | |
| | | @Range(min = 1800, max = 86400) |
| | | private long expireSec = RedisConst.DRC_MODE_ALIVE_SECOND; |
| | | |
| | | @Valid |
| | | private DeviceDrcInfoParam deviceInfo = new DeviceDrcInfoParam(); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.param; |
| | | |
| | | import com.dji.sample.control.model.enums.CameraModeEnum; |
| | | import com.dji.sample.control.model.enums.CameraTypeEnum; |
| | | import com.dji.sample.control.model.enums.GimbalResetModeEnum; |
| | | import lombok.Data; |
| | | import org.hibernate.validator.constraints.Range; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | import javax.validation.constraints.Pattern; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/1 |
| | | */ |
| | | @Data |
| | | public class DronePayloadParam { |
| | | |
| | | @Pattern(regexp = "\\d+-\\d+-\\d+") |
| | | @NotNull |
| | | private String payloadIndex; |
| | | |
| | | private CameraTypeEnum cameraType; |
| | | |
| | | @Range(min = 2, max = 200) |
| | | private Float zoomFactor; |
| | | |
| | | private CameraModeEnum cameraMode; |
| | | |
| | | /** |
| | | * true: Lock the gimbal, the gimbal and the drone rotate together. |
| | | * false: Only the gimbal rotates, but the drone does not. |
| | | */ |
| | | private Boolean locked; |
| | | |
| | | private Double pitchSpeed; |
| | | |
| | | /** |
| | | * Only valid when locked is false. |
| | | */ |
| | | private Double yawSpeed; |
| | | |
| | | /** |
| | | * upper left corner as center point |
| | | */ |
| | | @Range(min = 0, max = 1) |
| | | private Double x; |
| | | |
| | | @Range(min = 0, max = 1) |
| | | private Double y; |
| | | |
| | | private GimbalResetModeEnum resetMode; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.param; |
| | | |
| | | import com.dji.sample.control.model.dto.PointDTO; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | import org.hibernate.validator.constraints.Range; |
| | | |
| | | import javax.validation.Valid; |
| | | import javax.validation.constraints.NotNull; |
| | | import javax.validation.constraints.Size; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/2/14 |
| | | */ |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class FlyToPointParam { |
| | | |
| | | private String flyToId; |
| | | |
| | | @Range(min = 1, max = 15) |
| | | @NotNull |
| | | private Integer maxSpeed; |
| | | |
| | | /** |
| | | * The M30 series only support one point. |
| | | */ |
| | | @Size(min = 1) |
| | | @Valid |
| | | @NotNull |
| | | private List<PointDTO> points; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.param; |
| | | |
| | | import com.dji.sample.control.model.enums.PayloadCommandsEnum; |
| | | import lombok.Data; |
| | | |
| | | import javax.validation.Valid; |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/2 |
| | | */ |
| | | @Data |
| | | public class PayloadCommandsParam { |
| | | |
| | | private String sn; |
| | | |
| | | @NotNull |
| | | @Valid |
| | | private PayloadCommandsEnum cmd; |
| | | |
| | | @Valid |
| | | @NotNull |
| | | private DronePayloadParam data; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.model.param; |
| | | |
| | | import com.dji.sample.manage.model.enums.DroneRcLostActionEnum; |
| | | import com.dji.sample.manage.model.enums.WaylineRcLostActionEnum; |
| | | import lombok.Data; |
| | | import org.hibernate.validator.constraints.Range; |
| | | |
| | | import javax.validation.constraints.NotNull; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/1 |
| | | */ |
| | | @Data |
| | | public class TakeoffToPointParam { |
| | | |
| | | private String flightId; |
| | | |
| | | @Range(min = -180, max = 180) |
| | | @NotNull |
| | | private Double targetLongitude; |
| | | |
| | | @Range(min = -90, max = 90) |
| | | @NotNull |
| | | private Double targetLatitude; |
| | | |
| | | @Range(min = 2, max = 1500) |
| | | @NotNull |
| | | private Double targetHeight; |
| | | |
| | | @Range(min = 2, max = 1500) |
| | | @NotNull |
| | | private Double securityTakeoffHeight; |
| | | |
| | | @Range(min = 2, max = 1500) |
| | | @NotNull |
| | | private Double rthAltitude; |
| | | |
| | | @NotNull |
| | | private DroneRcLostActionEnum rcLostAction; |
| | | |
| | | @NotNull |
| | | private WaylineRcLostActionEnum exitWaylineWhenRcLost; |
| | | |
| | | @Range(min = 1, max = 15) |
| | | @NotNull |
| | | private Double maxSpeed; |
| | | } |
| | |
| | | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicReceiver; |
| | | import com.dji.sample.control.model.param.RemoteDebugParam; |
| | | import com.dji.sample.control.model.enums.DroneAuthorityEnum; |
| | | import com.dji.sample.control.model.param.*; |
| | | import org.springframework.messaging.MessageHeaders; |
| | | |
| | | /** |
| | |
| | | * @param param |
| | | * @return |
| | | */ |
| | | ResponseResult controlDock(String sn, String serviceIdentifier, RemoteDebugParam param); |
| | | ResponseResult controlDockDebug(String sn, String serviceIdentifier, RemoteDebugParam param); |
| | | |
| | | /** |
| | | * Handles multi-state command progress information. |
| | | * Make the drone fly to the target point. |
| | | * @param sn |
| | | * @param param |
| | | * @return |
| | | */ |
| | | ResponseResult flyToPoint(String sn, FlyToPointParam param); |
| | | |
| | | /** |
| | | * End the mission of flying the drone to the target point. |
| | | * @param sn |
| | | * @return |
| | | */ |
| | | ResponseResult flyToPointStop(String sn); |
| | | |
| | | /** |
| | | * Handle progress result notifications for fly to target point. |
| | | * @param receiver |
| | | * @param headers |
| | | * @return |
| | | */ |
| | | void handleControlProgress(CommonTopicReceiver receiver, MessageHeaders headers); |
| | | CommonTopicReceiver handleFlyToPointProgress(CommonTopicReceiver receiver, MessageHeaders headers); |
| | | |
| | | /** |
| | | * Control the drone to take off. |
| | | * @param sn |
| | | * @param param |
| | | * @return |
| | | */ |
| | | ResponseResult takeoffToPoint(String sn, TakeoffToPointParam param); |
| | | |
| | | /** |
| | | * Handle progress result notifications for takeoff to target point. |
| | | * @param receiver |
| | | * @param headers |
| | | * @return |
| | | */ |
| | | CommonTopicReceiver handleTakeoffToPointProgress(CommonTopicReceiver receiver, MessageHeaders headers); |
| | | |
| | | /** |
| | | * Seize the control authority of the drone or the payload control authority. |
| | | * @param sn |
| | | * @param authority |
| | | * @param param |
| | | * @return |
| | | */ |
| | | ResponseResult seizeAuthority(String sn, DroneAuthorityEnum authority, DronePayloadParam param); |
| | | |
| | | /** |
| | | * Control the payload of the drone. |
| | | * @param param |
| | | * @return |
| | | */ |
| | | ResponseResult payloadCommands(PayloadCommandsParam param) throws Exception; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.service; |
| | | |
| | | import com.dji.sample.control.model.dto.JwtAclDTO; |
| | | import com.dji.sample.control.model.dto.MqttBrokerDTO; |
| | | import com.dji.sample.control.model.param.DrcConnectParam; |
| | | import com.dji.sample.control.model.param.DrcModeParam; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/1/11 |
| | | */ |
| | | public interface IDrcService { |
| | | |
| | | /** |
| | | * Save the drc mode of dock in redis. |
| | | * @param dockSn |
| | | * @param clientId |
| | | */ |
| | | void setDrcModeInRedis(String dockSn, String clientId); |
| | | |
| | | /** |
| | | * Query the client that is controlling the dock. |
| | | * @param dockSn |
| | | * @return clientId |
| | | */ |
| | | String getDrcModeInRedis(String dockSn); |
| | | |
| | | /** |
| | | * Delete the drc mode of dock in redis. |
| | | * @param dockSn |
| | | * @return |
| | | */ |
| | | Boolean delDrcModeInRedis(String dockSn); |
| | | |
| | | /** |
| | | * Provide mqtt options for the control terminal. |
| | | * @param workspaceId |
| | | * @param userId |
| | | * @param username |
| | | * @param param |
| | | * @return |
| | | */ |
| | | MqttBrokerDTO userDrcAuth(String workspaceId, String userId, String username, DrcConnectParam param); |
| | | |
| | | /** |
| | | * Make the dock enter drc mode. And grant relevant permissions. |
| | | * @param workspaceId |
| | | * @param param |
| | | * @return |
| | | */ |
| | | JwtAclDTO deviceDrcEnter(String workspaceId, DrcModeParam param); |
| | | |
| | | /** |
| | | * Make the dock exit drc mode. |
| | | * @param workspaceId |
| | | * @param param |
| | | */ |
| | | void deviceDrcExit(String workspaceId, DrcModeParam param); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.service.impl; |
| | | |
| | | import com.dji.sample.control.model.param.DronePayloadParam; |
| | | |
| | | import java.util.Objects; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/4/23 |
| | | */ |
| | | public class CameraAimImpl extends PayloadCommandsHandler { |
| | | |
| | | public CameraAimImpl(DronePayloadParam param) { |
| | | super(param); |
| | | } |
| | | |
| | | @Override |
| | | public boolean valid() { |
| | | return Objects.nonNull(param.getX()) && Objects.nonNull(param.getY()) |
| | | && Objects.nonNull(param.getLocked()) && Objects.nonNull(param.getCameraType()); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.service.impl; |
| | | |
| | | import com.dji.sample.control.model.enums.CameraStateEnum; |
| | | import com.dji.sample.control.model.enums.CameraTypeEnum; |
| | | import com.dji.sample.control.model.param.DronePayloadParam; |
| | | |
| | | import java.util.Objects; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/4/23 |
| | | */ |
| | | public class CameraFocalLengthSetImpl extends PayloadCommandsHandler { |
| | | |
| | | public CameraFocalLengthSetImpl(DronePayloadParam param) { |
| | | super(param); |
| | | } |
| | | |
| | | @Override |
| | | public boolean valid() { |
| | | return Objects.nonNull(param.getCameraType()) && Objects.nonNull(param.getZoomFactor()) |
| | | && (CameraTypeEnum.ZOOM == param.getCameraType() |
| | | || CameraTypeEnum.IR == param.getCameraType()); |
| | | } |
| | | |
| | | @Override |
| | | public boolean canPublish(String deviceSn) { |
| | | super.canPublish(deviceSn); |
| | | if (CameraStateEnum.WORKING == osdCamera.getPhotoState()) { |
| | | return false; |
| | | } |
| | | switch (param.getCameraType()) { |
| | | case IR: |
| | | return param.getZoomFactor().intValue() != osdCamera.getIrZoomFactor(); |
| | | case ZOOM: |
| | | return param.getZoomFactor().intValue() != osdCamera.getZoomFactor(); |
| | | } |
| | | return false; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.service.impl; |
| | | |
| | | import com.dji.sample.control.model.enums.CameraStateEnum; |
| | | import com.dji.sample.control.model.param.DronePayloadParam; |
| | | |
| | | import java.util.Objects; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/4/23 |
| | | */ |
| | | public class CameraModeSwitchImpl extends PayloadCommandsHandler { |
| | | |
| | | public CameraModeSwitchImpl(DronePayloadParam param) { |
| | | super(param); |
| | | } |
| | | |
| | | @Override |
| | | public boolean valid() { |
| | | return Objects.nonNull(param.getCameraMode()); |
| | | } |
| | | |
| | | @Override |
| | | public boolean canPublish(String deviceSn) { |
| | | super.canPublish(deviceSn); |
| | | return param.getCameraMode() != osdCamera.getCameraMode() |
| | | && CameraStateEnum.IDLE == osdCamera.getPhotoState() |
| | | && CameraStateEnum.IDLE == osdCamera.getRecordingState(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.service.impl; |
| | | |
| | | import com.dji.sample.control.model.enums.CameraStateEnum; |
| | | import com.dji.sample.control.model.param.DronePayloadParam; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/4/23 |
| | | */ |
| | | public class CameraPhotoTakeImpl extends PayloadCommandsHandler { |
| | | |
| | | public CameraPhotoTakeImpl(DronePayloadParam param) { |
| | | super(param); |
| | | } |
| | | |
| | | @Override |
| | | public boolean canPublish(String deviceSn) { |
| | | super.canPublish(deviceSn); |
| | | return CameraStateEnum.WORKING != osdCamera.getPhotoState() && osdCamera.getRemainPhotoNum() > 0; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.service.impl; |
| | | |
| | | import com.dji.sample.control.model.enums.CameraModeEnum; |
| | | import com.dji.sample.control.model.enums.CameraStateEnum; |
| | | import com.dji.sample.control.model.param.DronePayloadParam; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/4/23 |
| | | */ |
| | | public class CameraRecordingStartImpl extends PayloadCommandsHandler { |
| | | |
| | | public CameraRecordingStartImpl(DronePayloadParam param) { |
| | | super(param); |
| | | } |
| | | |
| | | @Override |
| | | public boolean canPublish(String deviceSn) { |
| | | super.canPublish(deviceSn); |
| | | return CameraModeEnum.VIDEO == osdCamera.getCameraMode() |
| | | && CameraStateEnum.IDLE == osdCamera.getRecordingState() |
| | | && osdCamera.getRemainRecordDuration() > 0; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.service.impl; |
| | | |
| | | import com.dji.sample.control.model.enums.CameraStateEnum; |
| | | import com.dji.sample.control.model.param.DronePayloadParam; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/4/23 |
| | | */ |
| | | public class CameraRecordingStopImpl extends PayloadCommandsHandler { |
| | | |
| | | public CameraRecordingStopImpl(DronePayloadParam param) { |
| | | super(param); |
| | | } |
| | | |
| | | @Override |
| | | public boolean canPublish(String deviceSn) { |
| | | super.canPublish(deviceSn); |
| | | return CameraStateEnum.WORKING == osdCamera.getRecordingState(); |
| | | } |
| | | } |
| | |
| | | import com.dji.sample.component.mqtt.service.IMessageSenderService; |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import com.dji.sample.component.redis.RedisOpsUtils; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | import com.dji.sample.component.websocket.model.BizCodeEnum; |
| | | import com.dji.sample.component.websocket.service.ISendMessageService; |
| | | import com.dji.sample.component.websocket.service.IWebSocketManageService; |
| | | import com.dji.sample.control.model.enums.RemoteControlMethodEnum; |
| | | import com.dji.sample.control.model.param.RemoteDebugParam; |
| | | import com.dji.sample.control.model.dto.FlyToProgressReceiver; |
| | | import com.dji.sample.control.model.dto.ResultNotifyDTO; |
| | | import com.dji.sample.control.model.dto.TakeoffProgressReceiver; |
| | | import com.dji.sample.control.model.enums.DroneAuthorityEnum; |
| | | import com.dji.sample.control.model.enums.DroneControlMethodEnum; |
| | | import com.dji.sample.control.model.enums.RemoteDebugMethodEnum; |
| | | import com.dji.sample.control.model.param.*; |
| | | import com.dji.sample.control.service.IControlService; |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.model.enums.DeviceModeCodeEnum; |
| | | import com.dji.sample.manage.model.enums.DockModeCodeEnum; |
| | | import com.dji.sample.manage.model.enums.UserTypeEnum; |
| | | import com.dji.sample.manage.model.receiver.BasicDeviceProperty; |
| | | import com.dji.sample.manage.service.IDevicePayloadService; |
| | | import com.dji.sample.manage.service.IDeviceRedisService; |
| | | import com.dji.sample.manage.service.IDeviceService; |
| | | import com.dji.sample.wayline.model.enums.WaylineErrorCodeEnum; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.integration.annotation.ServiceActivator; |
| | | import org.springframework.integration.mqtt.support.MqttHeaders; |
| | | import org.springframework.messaging.MessageHeaders; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.Objects; |
| | | import java.util.Optional; |
| | | import java.util.UUID; |
| | | |
| | | /** |
| | |
| | | private ISendMessageService webSocketMessageService; |
| | | |
| | | @Autowired |
| | | private IWebSocketManageService webSocketManageService; |
| | | private IDeviceService deviceService; |
| | | |
| | | @Autowired |
| | | private IDeviceService deviceService; |
| | | private IDeviceRedisService deviceRedisService; |
| | | |
| | | @Autowired |
| | | private ObjectMapper mapper; |
| | | |
| | | @Autowired |
| | | private IDevicePayloadService devicePayloadService; |
| | | |
| | | private RemoteDebugHandler checkDebugCondition(String sn, RemoteDebugParam param, RemoteDebugMethodEnum controlMethodEnum) { |
| | | RemoteDebugHandler handler = Objects.nonNull(controlMethodEnum.getClazz()) ? |
| | | mapper.convertValue(Objects.nonNull(param) ? param : new Object(), controlMethodEnum.getClazz()) |
| | | : new RemoteDebugHandler(); |
| | | if (!handler.canPublish(sn)) { |
| | | throw new RuntimeException("The current state of the dock does not support this function."); |
| | | } |
| | | if (Objects.nonNull(param) && !handler.valid()) { |
| | | throw new RuntimeException(CommonErrorEnum.ILLEGAL_ARGUMENT.getErrorMsg()); |
| | | } |
| | | return handler; |
| | | } |
| | | |
| | | @Override |
| | | public ResponseResult controlDock(String sn, String serviceIdentifier, RemoteDebugParam param) { |
| | | RemoteControlMethodEnum controlMethodEnum = RemoteControlMethodEnum.find(serviceIdentifier); |
| | | if (RemoteControlMethodEnum.UNKNOWN == controlMethodEnum) { |
| | | public ResponseResult controlDockDebug(String sn, String serviceIdentifier, RemoteDebugParam param) { |
| | | RemoteDebugMethodEnum controlMethodEnum = RemoteDebugMethodEnum.find(serviceIdentifier); |
| | | if (RemoteDebugMethodEnum.UNKNOWN == controlMethodEnum) { |
| | | return ResponseResult.error("The " + serviceIdentifier + " method does not exist."); |
| | | } |
| | | |
| | | Object data = ""; |
| | | // Add parameter validation. |
| | | if (Objects.nonNull(controlMethodEnum.getClazz())) { |
| | | if (Objects.isNull(param)) { |
| | | return ResponseResult.error(CommonErrorEnum.ILLEGAL_ARGUMENT); |
| | | } |
| | | BasicDeviceProperty basicDeviceProperty = mapper.convertValue(param.getAction(), controlMethodEnum.getClazz()); |
| | | if (!basicDeviceProperty.valid()) { |
| | | return ResponseResult.error(CommonErrorEnum.ILLEGAL_ARGUMENT); |
| | | } |
| | | data = basicDeviceProperty; |
| | | } |
| | | RemoteDebugHandler data = checkDebugCondition(sn, param, controlMethodEnum); |
| | | |
| | | boolean isExist = deviceService.checkDeviceOnline(sn); |
| | | boolean isExist = deviceRedisService.checkDeviceOnline(sn); |
| | | if (!isExist) { |
| | | return ResponseResult.error("The dock is offline."); |
| | | } |
| | | String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + sn + TopicConst.SERVICES_SUF; |
| | | String bid = UUID.randomUUID().toString(); |
| | | ServiceReply serviceReplyOpt = messageSenderService.publishWithReply( |
| | | topic, CommonTopicResponse.builder() |
| | | .tid(UUID.randomUUID().toString()) |
| | | .bid(bid) |
| | | .method(serviceIdentifier) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data(data) |
| | | .build()); |
| | | ServiceReply serviceReply = messageSenderService.publishServicesTopic(sn, serviceIdentifier, data, bid); |
| | | |
| | | ServiceReply<EventsOutputReceiver> serviceReply = mapper.convertValue( |
| | | serviceReplyOpt, new TypeReference<ServiceReply<EventsOutputReceiver>>() {}); |
| | | if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) { |
| | | return ResponseResult.error(serviceReply.getResult(), |
| | | Objects.nonNull(serviceReply.getOutput()) ? serviceReply.getOutput().getStatus() |
| | | : "error: " + serviceIdentifier + serviceReply.getResult()); |
| | | "error: " + serviceIdentifier + serviceReply.getResult()); |
| | | } |
| | | if (controlMethodEnum.getProgress()) { |
| | | RedisOpsUtils.setWithExpire(serviceIdentifier + RedisConst.DELIMITER + bid, sn, |
| | |
| | | return ResponseResult.success(); |
| | | } |
| | | |
| | | @Override |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS, outputChannel = ChannelName.OUTBOUND) |
| | | public void handleControlProgress(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | /** |
| | | * Handles multi-state command progress information. |
| | | * @param receiver |
| | | * @param headers |
| | | * @return |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_CONTROL_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) |
| | | public CommonTopicReceiver handleControlProgress(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | String key = receiver.getMethod() + RedisConst.DELIMITER + receiver.getBid(); |
| | | if (RedisOpsUtils.getExpire(key) <= 0) { |
| | | return; |
| | | return receiver; |
| | | } |
| | | String sn = RedisOpsUtils.get(key).toString(); |
| | | |
| | | EventsReceiver<EventsOutputReceiver> eventsReceiver = mapper.convertValue(receiver.getData(), |
| | | new TypeReference<EventsReceiver<EventsOutputReceiver>>(){}); |
| | | EventsReceiver<EventsOutputProgressReceiver> eventsReceiver = mapper.convertValue(receiver.getData(), |
| | | new TypeReference<EventsReceiver<EventsOutputProgressReceiver>>(){}); |
| | | eventsReceiver.setBid(receiver.getBid()); |
| | | eventsReceiver.setSn(sn); |
| | | |
| | |
| | | RedisOpsUtils.del(key); |
| | | } |
| | | |
| | | DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + sn); |
| | | webSocketMessageService.sendBatch( |
| | | webSocketManageService.getValueWithWorkspaceAndUserType( |
| | | device.getWorkspaceId(), UserTypeEnum.WEB.getVal()), |
| | | CustomWebSocketMessage.builder() |
| | | .data(eventsReceiver) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(receiver.getMethod()) |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(sn); |
| | | |
| | | if (deviceOpt.isEmpty()) { |
| | | throw new RuntimeException("The device is offline."); |
| | | } |
| | | |
| | | DeviceDTO device = deviceOpt.get(); |
| | | webSocketMessageService.sendBatch(device.getWorkspaceId(), UserTypeEnum.WEB.getVal(), |
| | | receiver.getMethod(), eventsReceiver); |
| | | |
| | | return receiver; |
| | | } |
| | | |
| | | private void checkFlyToCondition(String dockSn) { |
| | | // TODO 设备固件版本不兼容情况 |
| | | Optional<DeviceDTO> dockOpt = deviceRedisService.getDeviceOnline(dockSn); |
| | | if (dockOpt.isEmpty()) { |
| | | throw new RuntimeException("The dock is offline, please restart the dock."); |
| | | } |
| | | |
| | | DeviceModeCodeEnum deviceMode = deviceService.getDeviceMode(dockOpt.get().getChildDeviceSn()); |
| | | if (DeviceModeCodeEnum.MANUAL != deviceMode) { |
| | | throw new RuntimeException("The current state of the drone does not support this function, please try again later."); |
| | | } |
| | | |
| | | ResponseResult result = seizeAuthority(dockSn, DroneAuthorityEnum.FLIGHT, null); |
| | | if (ResponseResult.CODE_SUCCESS != result.getCode()) { |
| | | throw new IllegalArgumentException(result.getMessage()); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public ResponseResult flyToPoint(String sn, FlyToPointParam param) { |
| | | checkFlyToCondition(sn); |
| | | |
| | | param.setFlyToId(UUID.randomUUID().toString()); |
| | | ServiceReply reply = messageSenderService.publishServicesTopic(sn, DroneControlMethodEnum.FLY_TO_POINT.getMethod(), param, param.getFlyToId()); |
| | | return ResponseResult.CODE_SUCCESS != reply.getResult() ? |
| | | ResponseResult.error("Flying to the target point failed." + reply.getResult()) |
| | | : ResponseResult.success(); |
| | | } |
| | | |
| | | @Override |
| | | public ResponseResult flyToPointStop(String sn) { |
| | | ServiceReply reply = messageSenderService.publishServicesTopic(sn, DroneControlMethodEnum.FLY_TO_POINT_STOP.getMethod(), null); |
| | | return ResponseResult.CODE_SUCCESS != reply.getResult() ? |
| | | ResponseResult.error("The drone flying to the target point failed to stop. " + reply.getResult()) |
| | | : ResponseResult.success(); |
| | | } |
| | | |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FLY_TO_POINT_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) |
| | | public CommonTopicReceiver handleFlyToPointProgress(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | String dockSn = receiver.getGateway(); |
| | | |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(dockSn); |
| | | if (deviceOpt.isEmpty()) { |
| | | log.error("The dock is offline."); |
| | | return null; |
| | | } |
| | | |
| | | FlyToProgressReceiver eventsReceiver = mapper.convertValue(receiver.getData(), new TypeReference<FlyToProgressReceiver>(){}); |
| | | webSocketMessageService.sendBatch(deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), |
| | | BizCodeEnum.FLY_TO_POINT_PROGRESS.getCode(), |
| | | ResultNotifyDTO.builder().sn(dockSn) |
| | | .message(WaylineErrorCodeEnum.SUCCESS == eventsReceiver.getResult() ? |
| | | eventsReceiver.getStatus().getMessage() : eventsReceiver.getResult().getErrorMsg()) |
| | | .result(eventsReceiver.getResult().getErrorCode()) |
| | | .build()); |
| | | return receiver; |
| | | } |
| | | |
| | | private void checkTakeoffCondition(String dockSn) { |
| | | Optional<DeviceDTO> dockOpt = deviceRedisService.getDeviceOnline(dockSn); |
| | | if (dockOpt.isEmpty() || DockModeCodeEnum.IDLE != deviceService.getDockMode(dockSn)) { |
| | | throw new RuntimeException("The current state does not support takeoff."); |
| | | } |
| | | |
| | | ResponseResult result = seizeAuthority(dockSn, DroneAuthorityEnum.FLIGHT, null); |
| | | if (ResponseResult.CODE_SUCCESS != result.getCode()) { |
| | | throw new IllegalArgumentException(result.getMessage()); |
| | | } |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public ResponseResult takeoffToPoint(String sn, TakeoffToPointParam param) { |
| | | checkTakeoffCondition(sn); |
| | | |
| | | param.setFlightId(UUID.randomUUID().toString()); |
| | | ServiceReply reply = messageSenderService.publishServicesTopic(sn, DroneControlMethodEnum.TAKE_OFF_TO_POINT.getMethod(), param, param.getFlightId()); |
| | | return ResponseResult.CODE_SUCCESS != reply.getResult() ? |
| | | ResponseResult.error("The drone failed to take off. " + reply.getResult()) |
| | | : ResponseResult.success(); |
| | | } |
| | | |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_TAKE_OFF_TO_POINT_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) |
| | | public CommonTopicReceiver handleTakeoffToPointProgress(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | String dockSn = receiver.getGateway(); |
| | | |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(dockSn); |
| | | if (deviceOpt.isEmpty()) { |
| | | log.error("The dock is offline."); |
| | | return null; |
| | | } |
| | | TakeoffProgressReceiver eventsReceiver = mapper.convertValue(receiver.getData(), new TypeReference<TakeoffProgressReceiver>(){}); |
| | | |
| | | webSocketMessageService.sendBatch(deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), |
| | | BizCodeEnum.TAKE_OFF_TO_POINT_PROGRESS.getCode(), |
| | | ResultNotifyDTO.builder().sn(dockSn) |
| | | .message(WaylineErrorCodeEnum.SUCCESS == eventsReceiver.getResult() ? |
| | | eventsReceiver.getStatus().getMessage() : eventsReceiver.getResult().getErrorMsg()) |
| | | .result(eventsReceiver.getResult().getErrorCode()) |
| | | .build()); |
| | | |
| | | if (receiver.getNeedReply() != null && receiver.getNeedReply() == 1) { |
| | | String topic = headers.get(MqttHeaders.RECEIVED_TOPIC) + TopicConst._REPLY_SUF; |
| | | messageSenderService.publish(topic, |
| | | CommonTopicResponse.builder() |
| | | .tid(receiver.getTid()) |
| | | .bid(receiver.getBid()) |
| | | .method(receiver.getMethod()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data(RequestsReply.success()) |
| | | .build()); |
| | | return receiver; |
| | | } |
| | | |
| | | @Override |
| | | public ResponseResult seizeAuthority(String sn, DroneAuthorityEnum authority, DronePayloadParam param) { |
| | | String method; |
| | | switch (authority) { |
| | | case FLIGHT: |
| | | if (deviceService.checkAuthorityFlight(sn)) { |
| | | return ResponseResult.success(); |
| | | } |
| | | method = DroneControlMethodEnum.FLIGHT_AUTHORITY_GRAB.getMethod(); |
| | | break; |
| | | case PAYLOAD: |
| | | if (checkPayloadAuthority(sn, param.getPayloadIndex())) { |
| | | return ResponseResult.success(); |
| | | } |
| | | method = DroneControlMethodEnum.PAYLOAD_AUTHORITY_GRAB.getMethod(); |
| | | break; |
| | | default: |
| | | return ResponseResult.error(CommonErrorEnum.ILLEGAL_ARGUMENT); |
| | | } |
| | | ServiceReply serviceReply = messageSenderService.publishServicesTopic(sn, method, param); |
| | | return ResponseResult.CODE_SUCCESS != serviceReply.getResult() ? |
| | | ResponseResult.error(serviceReply.getResult(), "Method: " + method + " Error Code:" + serviceReply.getResult()) |
| | | : ResponseResult.success(); |
| | | } |
| | | |
| | | private Boolean checkPayloadAuthority(String sn, String payloadIndex) { |
| | | Optional<DeviceDTO> dockOpt = deviceRedisService.getDeviceOnline(sn); |
| | | if (dockOpt.isEmpty()) { |
| | | throw new RuntimeException("The dock is offline, please restart the dock."); |
| | | } |
| | | return devicePayloadService.checkAuthorityPayload(dockOpt.get().getChildDeviceSn(), payloadIndex); |
| | | } |
| | | |
| | | |
| | | @Override |
| | | public ResponseResult payloadCommands(PayloadCommandsParam param) throws Exception { |
| | | param.getCmd().getClazz() |
| | | .getDeclaredConstructor(DronePayloadParam.class) |
| | | .newInstance(param.getData()) |
| | | .checkCondition(param.getSn()); |
| | | |
| | | ServiceReply serviceReply = messageSenderService.publishServicesTopic(param.getSn(), param.getCmd().getCmd(), param.getData()); |
| | | return ResponseResult.CODE_SUCCESS != serviceReply.getResult() ? |
| | | ResponseResult.error(serviceReply.getResult(), " Error Code:" + serviceReply.getResult()) |
| | | : ResponseResult.success(); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.service.impl; |
| | | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.mqtt.config.MqttConfiguration; |
| | | import com.dji.sample.component.mqtt.model.*; |
| | | import com.dji.sample.component.mqtt.service.IMessageSenderService; |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import com.dji.sample.component.redis.RedisOpsUtils; |
| | | import com.dji.sample.component.websocket.model.BizCodeEnum; |
| | | import com.dji.sample.component.websocket.service.ISendMessageService; |
| | | import com.dji.sample.control.model.dto.*; |
| | | import com.dji.sample.control.model.enums.DrcMethodEnum; |
| | | import com.dji.sample.control.model.enums.DrcStatusErrorEnum; |
| | | import com.dji.sample.control.model.enums.DroneAuthorityEnum; |
| | | import com.dji.sample.control.model.enums.MqttAclAccessEnum; |
| | | import com.dji.sample.control.model.param.DrcConnectParam; |
| | | import com.dji.sample.control.model.param.DrcModeParam; |
| | | import com.dji.sample.control.service.IControlService; |
| | | import com.dji.sample.control.service.IDrcService; |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.model.enums.DockModeCodeEnum; |
| | | import com.dji.sample.manage.model.enums.UserTypeEnum; |
| | | import com.dji.sample.manage.model.receiver.OsdSubDeviceReceiver; |
| | | import com.dji.sample.manage.service.IDeviceRedisService; |
| | | import com.dji.sample.manage.service.IDeviceService; |
| | | import com.dji.sample.wayline.model.dto.WaylineTaskProgressReceiver; |
| | | import com.dji.sample.wayline.model.enums.WaylineJobStatusEnum; |
| | | import com.dji.sample.wayline.model.enums.WaylineTaskStatusEnum; |
| | | import com.dji.sample.wayline.model.param.UpdateJobParam; |
| | | import com.dji.sample.wayline.service.IWaylineJobService; |
| | | import com.dji.sample.wayline.service.IWaylineRedisService; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.integration.annotation.ServiceActivator; |
| | | import org.springframework.messaging.MessageHeaders; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/1/11 |
| | | */ |
| | | @Service |
| | | @Slf4j |
| | | public class DrcServiceImpl implements IDrcService { |
| | | |
| | | @Autowired |
| | | private IMessageSenderService messageSenderService; |
| | | |
| | | @Autowired |
| | | private ObjectMapper objectMapper; |
| | | |
| | | @Autowired |
| | | private IWaylineJobService waylineJobService; |
| | | |
| | | @Autowired |
| | | private IDeviceService deviceService; |
| | | |
| | | @Autowired |
| | | private ObjectMapper mapper; |
| | | |
| | | @Autowired |
| | | private ISendMessageService webSocketMessageService; |
| | | |
| | | @Autowired |
| | | private IControlService controlService; |
| | | |
| | | @Autowired |
| | | private IDeviceRedisService deviceRedisService; |
| | | |
| | | @Autowired |
| | | private IWaylineRedisService waylineRedisService; |
| | | |
| | | @Override |
| | | public void setDrcModeInRedis(String dockSn, String clientId) { |
| | | RedisOpsUtils.setWithExpire(RedisConst.DRC_PREFIX + dockSn, clientId, RedisConst.DRC_MODE_ALIVE_SECOND); |
| | | } |
| | | |
| | | @Override |
| | | public String getDrcModeInRedis(String dockSn) { |
| | | return (String) RedisOpsUtils.get(RedisConst.DRC_PREFIX + dockSn); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean delDrcModeInRedis(String dockSn) { |
| | | return RedisOpsUtils.del(RedisConst.DRC_PREFIX + dockSn); |
| | | } |
| | | |
| | | @Override |
| | | public MqttBrokerDTO userDrcAuth(String workspaceId, String userId, String username, DrcConnectParam param) { |
| | | |
| | | // refresh token |
| | | String clientId = param.getClientId(); |
| | | // first time |
| | | if (!StringUtils.hasText(clientId) || !RedisOpsUtils.checkExist(RedisConst.MQTT_ACL_PREFIX + clientId)) { |
| | | clientId = userId + "-" + System.currentTimeMillis(); |
| | | RedisOpsUtils.hashSet(RedisConst.MQTT_ACL_PREFIX + clientId, "", MqttAclAccessEnum.ALL.getValue()); |
| | | } |
| | | |
| | | String key = RedisConst.MQTT_ACL_PREFIX + clientId; |
| | | |
| | | try { |
| | | RedisOpsUtils.expireKey(key, RedisConst.DRC_MODE_ALIVE_SECOND); |
| | | |
| | | return MqttConfiguration.getMqttBrokerWithDrc( |
| | | clientId, username, param.getExpireSec(), Collections.emptyMap()); |
| | | } catch (RuntimeException e) { |
| | | RedisOpsUtils.del(key); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | private void checkDrcModeCondition(String workspaceId, String dockSn) { |
| | | Optional<EventsReceiver<WaylineTaskProgressReceiver>> runningOpt = waylineRedisService.getRunningWaylineJob(dockSn); |
| | | if (runningOpt.isPresent() && WaylineJobStatusEnum.IN_PROGRESS == waylineJobService.getWaylineState(dockSn)) { |
| | | waylineJobService.updateJobStatus(workspaceId, runningOpt.get().getBid(), |
| | | UpdateJobParam.builder().status(WaylineTaskStatusEnum.PAUSE).build()); |
| | | } |
| | | |
| | | DockModeCodeEnum dockMode = deviceService.getDockMode(dockSn); |
| | | Optional<DeviceDTO> dockOpt = deviceRedisService.getDeviceOnline(dockSn); |
| | | if (dockOpt.isPresent() && (DockModeCodeEnum.IDLE == dockMode || DockModeCodeEnum.WORKING == dockMode)) { |
| | | Optional<OsdSubDeviceReceiver> deviceOsd = deviceRedisService.getDeviceOsd(dockOpt.get().getChildDeviceSn(), OsdSubDeviceReceiver.class); |
| | | if (deviceOsd.isEmpty() || deviceOsd.get().getElevation() <= 0) { |
| | | throw new RuntimeException("The drone is not in the sky and cannot enter command flight mode."); |
| | | } |
| | | } else { |
| | | throw new RuntimeException("The current state of the dock does not support entering command flight mode."); |
| | | } |
| | | |
| | | ResponseResult result = controlService.seizeAuthority(dockSn, DroneAuthorityEnum.FLIGHT, null); |
| | | if (ResponseResult.CODE_SUCCESS != result.getCode()) { |
| | | throw new IllegalArgumentException(result.getMessage()); |
| | | } |
| | | |
| | | } |
| | | |
| | | @Override |
| | | public JwtAclDTO deviceDrcEnter(String workspaceId, DrcModeParam param) { |
| | | String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + param.getDockSn() + TopicConst.DRC; |
| | | String pubTopic = topic + TopicConst.DOWN; |
| | | String subTopic = topic + TopicConst.UP; |
| | | |
| | | // If the dock is in drc mode, refresh the permissions directly. |
| | | if (deviceService.checkDockDrcMode(param.getDockSn()) |
| | | && param.getClientId().equals(this.getDrcModeInRedis(param.getDockSn()))) { |
| | | refreshAcl(param.getDockSn(), param.getClientId(), topic, subTopic); |
| | | return JwtAclDTO.builder().sub(List.of(subTopic)).pub(List.of(pubTopic)).build(); |
| | | } |
| | | |
| | | checkDrcModeCondition(workspaceId, param.getDockSn()); |
| | | |
| | | ServiceReply reply = messageSenderService.publishServicesTopic( |
| | | param.getDockSn(), DrcMethodEnum.DRC_MODE_ENTER.getMethod(), |
| | | DrcModeDTO.builder() |
| | | .mqttBroker(MqttConfiguration.getMqttBrokerWithDrc(param.getDockSn() + "-" + System.currentTimeMillis(), param.getDockSn(), |
| | | RedisConst.DRC_MODE_ALIVE_SECOND.longValue(), |
| | | Map.of(MapKeyConst.ACL, objectMapper.convertValue(JwtAclDTO.builder() |
| | | .pub(List.of(subTopic)) |
| | | .sub(List.of(pubTopic)) |
| | | .build(), new TypeReference<Map<String, ?>>() {})))) |
| | | .build()); |
| | | |
| | | if (ResponseResult.CODE_SUCCESS != reply.getResult()) { |
| | | throw new RuntimeException("SN: " + param.getDockSn() + "; Error Code:" + reply.getResult() + "; Failed to enter command flight control mode, please try again later!"); |
| | | } |
| | | |
| | | refreshAcl(param.getDockSn(), param.getClientId(), pubTopic, subTopic); |
| | | return JwtAclDTO.builder().sub(List.of(subTopic)).pub(List.of(pubTopic)).build(); |
| | | } |
| | | |
| | | private void refreshAcl(String dockSn, String clientId, String pubTopic, String subTopic) { |
| | | this.setDrcModeInRedis(dockSn, clientId); |
| | | |
| | | // assign acl,Match by clientId. https://www.emqx.io/docs/zh/v4.4/advanced/acl-redis.html |
| | | // scheme: HSET mqtt_acl:[clientid] [topic] [access] |
| | | String key = RedisConst.MQTT_ACL_PREFIX + clientId; |
| | | RedisOpsUtils.hashSet(key, pubTopic, MqttAclAccessEnum.PUB.getValue()); |
| | | RedisOpsUtils.hashSet(key, subTopic, MqttAclAccessEnum.SUB.getValue()); |
| | | } |
| | | |
| | | @Override |
| | | public void deviceDrcExit(String workspaceId, DrcModeParam param) { |
| | | if (!deviceService.checkDockDrcMode(param.getDockSn())) { |
| | | throw new RuntimeException("The dock is not in flight control mode."); |
| | | } |
| | | ServiceReply reply = messageSenderService.publishServicesTopic( |
| | | param.getDockSn(), DrcMethodEnum.DRC_MODE_EXIT.getMethod(), ""); |
| | | if (ResponseResult.CODE_SUCCESS != reply.getResult()) { |
| | | throw new RuntimeException("SN: " + param.getDockSn() + "; Error Code:" + |
| | | reply.getResult() + "; Failed to exit command flight control mode, please try again later!"); |
| | | } |
| | | |
| | | String jobId = waylineRedisService.getPausedWaylineJobId(param.getDockSn()); |
| | | if (StringUtils.hasText(jobId)) { |
| | | waylineJobService.updateJobStatus(workspaceId, jobId, UpdateJobParam.builder().status(WaylineTaskStatusEnum.RESUME).build()); |
| | | } |
| | | |
| | | this.delDrcModeInRedis(param.getDockSn()); |
| | | RedisOpsUtils.del(RedisConst.MQTT_ACL_PREFIX + param.getClientId()); |
| | | } |
| | | |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_DRC_STATUS_NOTIFY, outputChannel = ChannelName.OUTBOUND_EVENTS) |
| | | public CommonTopicReceiver handleDrcStatusNotify(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | String dockSn = receiver.getGateway(); |
| | | |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(dockSn); |
| | | if (deviceOpt.isEmpty()) { |
| | | return null; |
| | | } |
| | | |
| | | DrcStatusNotifyReceiver eventsReceiver = mapper.convertValue(receiver.getData(), new TypeReference<DrcStatusNotifyReceiver>(){}); |
| | | if (DrcStatusErrorEnum.SUCCESS != eventsReceiver.getResult()) { |
| | | webSocketMessageService.sendBatch( |
| | | deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), BizCodeEnum.DRC_STATUS_NOTIFY.getCode(), |
| | | ResultNotifyDTO.builder().sn(dockSn) |
| | | .message(eventsReceiver.getResult().getErrorMsg()) |
| | | .result(eventsReceiver.getResult().getErrorCode()).build()); |
| | | } |
| | | return receiver; |
| | | } |
| | | |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_DRC_MODE_EXIT_NOTIFY, outputChannel = ChannelName.OUTBOUND_EVENTS) |
| | | public CommonTopicReceiver handleDrcModeExitNotify(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | String dockSn = receiver.getGateway(); |
| | | |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(dockSn); |
| | | if (deviceOpt.isEmpty()) { |
| | | return null; |
| | | } |
| | | |
| | | DrcModeReasonReceiver eventsReceiver = mapper.convertValue(receiver.getData(), new TypeReference<DrcModeReasonReceiver>(){}); |
| | | webSocketMessageService.sendBatch( |
| | | deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), BizCodeEnum.JOYSTICK_INVALID_NOTIFY.getCode(), |
| | | ResultNotifyDTO.builder().sn(dockSn) |
| | | .message(eventsReceiver.getReason().getMessage()) |
| | | .result(eventsReceiver.getReason().getVal()).build()); |
| | | return receiver; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.service.impl; |
| | | |
| | | import com.dji.sample.control.model.param.DronePayloadParam; |
| | | |
| | | import java.util.Objects; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/4/23 |
| | | */ |
| | | public class GimbalResetImpl extends PayloadCommandsHandler { |
| | | public GimbalResetImpl(DronePayloadParam param) { |
| | | super(param); |
| | | } |
| | | |
| | | @Override |
| | | public boolean valid() { |
| | | return Objects.nonNull(param.getResetMode()); |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.service.impl; |
| | | |
| | | import com.dji.sample.common.util.SpringBeanUtils; |
| | | import com.dji.sample.control.model.param.DronePayloadParam; |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.model.receiver.OsdCameraReceiver; |
| | | import com.dji.sample.manage.model.receiver.OsdSubDeviceReceiver; |
| | | import com.dji.sample.manage.service.IDevicePayloadService; |
| | | import com.dji.sample.manage.service.IDeviceRedisService; |
| | | |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/4/23 |
| | | */ |
| | | public abstract class PayloadCommandsHandler { |
| | | |
| | | DronePayloadParam param; |
| | | |
| | | OsdCameraReceiver osdCamera; |
| | | |
| | | PayloadCommandsHandler(DronePayloadParam param) { |
| | | this.param = param; |
| | | } |
| | | |
| | | public boolean valid() { |
| | | return true; |
| | | } |
| | | |
| | | public boolean canPublish(String deviceSn) { |
| | | Optional<OsdSubDeviceReceiver> deviceOpt = SpringBeanUtils.getBean(IDeviceRedisService.class) |
| | | .getDeviceOsd(deviceSn, OsdSubDeviceReceiver.class); |
| | | if (deviceOpt.isEmpty()) { |
| | | throw new RuntimeException("The device is offline."); |
| | | } |
| | | osdCamera = deviceOpt.get().getCameras().stream() |
| | | .filter(osdCamera -> param.getPayloadIndex().equals(osdCamera.getPayloadIndex())) |
| | | .findAny() |
| | | .orElseThrow(() -> new RuntimeException("Did not receive osd information about the camera, please check the cache data.")); |
| | | return true; |
| | | } |
| | | |
| | | private String checkDockOnline(String dockSn) { |
| | | Optional<DeviceDTO> deviceOpt = SpringBeanUtils.getBean(IDeviceRedisService.class).getDeviceOnline(dockSn); |
| | | if (deviceOpt.isEmpty()) { |
| | | throw new RuntimeException("The dock is offline."); |
| | | } |
| | | return deviceOpt.get().getChildDeviceSn(); |
| | | } |
| | | |
| | | private void checkDeviceOnline(String deviceSn) { |
| | | boolean isOnline = SpringBeanUtils.getBean(IDeviceRedisService.class).checkDeviceOnline(deviceSn); |
| | | if (!isOnline) { |
| | | throw new RuntimeException("The device is offline."); |
| | | } |
| | | } |
| | | |
| | | private void checkAuthority(String deviceSn) { |
| | | boolean hasAuthority = SpringBeanUtils.getBean(IDevicePayloadService.class) |
| | | .checkAuthorityPayload(deviceSn, param.getPayloadIndex()); |
| | | if (!hasAuthority) { |
| | | throw new RuntimeException("The device does not have payload control authority."); |
| | | } |
| | | } |
| | | |
| | | public final void checkCondition(String dockSn) { |
| | | if (!valid()) { |
| | | throw new RuntimeException("illegal argument"); |
| | | } |
| | | |
| | | String deviceSn = checkDockOnline(dockSn); |
| | | checkDeviceOnline(deviceSn); |
| | | checkAuthority(deviceSn); |
| | | |
| | | if (!canPublish(deviceSn)) { |
| | | throw new RuntimeException("The current state of the drone does not support this function, please try again later."); |
| | | } |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.control.service.impl; |
| | | |
| | | import com.dji.sample.common.util.SpringBeanUtils; |
| | | import com.dji.sample.manage.model.enums.DockModeCodeEnum; |
| | | import com.dji.sample.manage.service.IDeviceService; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/10/27 |
| | | */ |
| | | public class RemoteDebugHandler { |
| | | |
| | | public boolean valid() { |
| | | return false; |
| | | } |
| | | |
| | | public boolean canPublish(String sn) { |
| | | IDeviceService deviceService = SpringBeanUtils.getBean(IDeviceService.class); |
| | | DockModeCodeEnum dockMode = deviceService.getDockMode(sn); |
| | | return DockModeCodeEnum.REMOTE_DEBUGGING == dockMode; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.dto; |
| | | |
| | | import com.dji.sample.control.model.enums.DroneAuthorityEnum; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/2 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class DeviceAuthorityDTO { |
| | | |
| | | private String sn; |
| | | |
| | | private DroneAuthorityEnum type; |
| | | |
| | | private String controlSource; |
| | | |
| | | } |
| | |
| | | |
| | | private String workspaceId; |
| | | |
| | | private String deviceIndex; |
| | | private String controlSource; |
| | | |
| | | private String deviceDesc; |
| | | |
| | |
| | | private Integer firmwareStatus; |
| | | |
| | | private Integer firmwareProgress; |
| | | |
| | | private String parentSn; |
| | | } |
| | |
| | | |
| | | private String payloadName; |
| | | |
| | | private Integer payloadIndex; |
| | | private Integer index; |
| | | |
| | | private String payloadDesc; |
| | | |
| | | private String controlSource; |
| | | |
| | | private String payloadIndex; |
| | | } |
| | |
| | | @TableField(value = "payload_desc") |
| | | private String payloadDesc; |
| | | |
| | | @TableField(value = "control_source") |
| | | private String controlSource; |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.enums; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | import com.fasterxml.jackson.annotation.JsonValue; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/16 |
| | | */ |
| | | public enum ControlSourceEnum { |
| | | |
| | | A, B; |
| | | |
| | | @JsonValue |
| | | public String getControlSource() { |
| | | return name(); |
| | | } |
| | | |
| | | @JsonCreator |
| | | public static ControlSourceEnum find(String controlSource) { |
| | | return Arrays.stream(values()).filter(controlSourceEnum -> controlSourceEnum.name().equals(controlSource)).findAny().get(); |
| | | } |
| | | } |
| | |
| | | |
| | | import lombok.Getter; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * |
| | | * @author sean.zhou |
| | |
| | | |
| | | PAYLOAD(1), |
| | | |
| | | DOCK (3); |
| | | DOCK (3), |
| | | |
| | | UNKNOWN(-1); |
| | | |
| | | int val; |
| | | |
| | | DeviceDomainEnum(int val) { |
| | | this.val = val; |
| | | } |
| | | |
| | | public static DeviceDomainEnum find(int val) { |
| | | return Arrays.stream(values()).filter(domainEnum -> domainEnum.val == val).findAny().orElse(UNKNOWN); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.enums; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | import com.fasterxml.jackson.annotation.JsonValue; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/9 |
| | | */ |
| | | public enum DeviceModeCodeEnum { |
| | | |
| | | IDLE(0), |
| | | |
| | | TAKEOFF_PREPARE(1), |
| | | |
| | | TAKEOFF_FINISHED(2), |
| | | |
| | | MANUAL(3), |
| | | |
| | | TAKEOFF_AUTO(4), |
| | | |
| | | WAYLINE(5), |
| | | |
| | | PANORAMIC_SHOT(6), |
| | | |
| | | ACTIVE_TRACK(7), |
| | | |
| | | ADS_B_AVOIDANCE(8), |
| | | |
| | | RETURN_AUTO(9), |
| | | |
| | | LANDING_AUTO(10), |
| | | |
| | | LANDING_FORCED(11), |
| | | |
| | | LANDING_THREE_PROPELLER(12), |
| | | |
| | | UPGRADING(13), |
| | | |
| | | DISCONNECTED(14), |
| | | |
| | | APAS(15), |
| | | |
| | | VIRTUAL_JOYSTICK(16); |
| | | |
| | | int val; |
| | | |
| | | DeviceModeCodeEnum(int val) { |
| | | this.val = val; |
| | | } |
| | | |
| | | @JsonValue |
| | | public int getVal() { |
| | | return val; |
| | | } |
| | | |
| | | @JsonCreator |
| | | public static DeviceModeCodeEnum find(int value) { |
| | | return Arrays.stream(values()).filter(modeCodeEnum -> modeCodeEnum.ordinal() == value).findAny().orElse(DISCONNECTED); |
| | | } |
| | | } |
| | |
| | | |
| | | DISTANCE_LIMIT_STATUS("distance_limit_status", DistanceLimitStatusReceiver.class), |
| | | |
| | | OBSTACLE_AVOIDANCE("obstacle_avoidance", ObstacleAvoidanceReceiver.class); |
| | | OBSTACLE_AVOIDANCE("obstacle_avoidance", ObstacleAvoidanceReceiver.class), |
| | | |
| | | RTH_ALTITUDE("rth_altitude", RthAltitudeReceiver.class), |
| | | |
| | | OUT_OF_CONTROL_ACTION("out_of_control_action", OutOfControlActionReceiver.class); |
| | | |
| | | String property; |
| | | |
| New file |
| | |
| | | package com.dji.sample.manage.model.enums; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | import com.fasterxml.jackson.annotation.JsonValue; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/2/28 |
| | | */ |
| | | public enum DockDrcStateEnum { |
| | | |
| | | DISCONNECTED(0), |
| | | |
| | | CONNECTING(1), |
| | | |
| | | CONNECTED(2); |
| | | |
| | | int val; |
| | | |
| | | DockDrcStateEnum(int val) { |
| | | this.val = val; |
| | | } |
| | | |
| | | @JsonValue |
| | | public int getVal() { |
| | | return val; |
| | | } |
| | | |
| | | @JsonCreator |
| | | public static DockDrcStateEnum find(int val) { |
| | | return Arrays.stream(values()).filter(drcState -> drcState.getVal() == val).findAny().orElse(DISCONNECTED); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.enums; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | import com.fasterxml.jackson.annotation.JsonValue; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/2/28 |
| | | */ |
| | | public enum DockModeCodeEnum { |
| | | |
| | | IDLE(0), |
| | | |
| | | DEBUGGING(1), |
| | | |
| | | REMOTE_DEBUGGING(2), |
| | | |
| | | UPGRADING(3), |
| | | |
| | | WORKING(4), |
| | | |
| | | DISCONNECTED(-1); |
| | | |
| | | int val; |
| | | |
| | | DockModeCodeEnum(int val) { |
| | | this.val = val; |
| | | } |
| | | |
| | | @JsonValue |
| | | public int getVal() { |
| | | return val; |
| | | } |
| | | |
| | | @JsonCreator |
| | | public static DockModeCodeEnum find(int val) { |
| | | return Arrays.stream(values()).filter(modeCode -> modeCode.getVal() == val).findAny().orElse(DISCONNECTED); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.enums; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | import com.fasterxml.jackson.annotation.JsonValue; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/1 |
| | | */ |
| | | public enum DroneRcLostActionEnum { |
| | | |
| | | HOVER, LAND, RETURN_HOME; |
| | | |
| | | @JsonValue |
| | | public int getVal() { |
| | | return ordinal(); |
| | | } |
| | | |
| | | @JsonCreator |
| | | public static DroneRcLostActionEnum find(int val) { |
| | | return Arrays.stream(values()).filter(controlActionEnum -> controlActionEnum.ordinal() == val).findAny().get(); |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | @Getter |
| | | public enum DomainType { |
| | | DRONE_NEST("drone_nest"), |
| | | |
| | | DRONE("drone"); |
| | | |
| | | private String domain; |
| | | |
| | | DomainType(String domain) { |
| | | this.domain = domain; |
| | | } |
| | | |
| | | } |
| | | |
| | | @Getter |
| | | public enum HmsFaqIdEnum { |
| | | |
| | | DOCK_TIP("dock_tip_"), |
| New file |
| | |
| | | package com.dji.sample.manage.model.enums; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | import com.fasterxml.jackson.annotation.JsonValue; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/15 |
| | | */ |
| | | public enum WaylineRcLostActionEnum { |
| | | |
| | | CONTINUE_WAYLINE, EXECUTE_RC_LOST_ACTION; |
| | | |
| | | @JsonValue |
| | | public int getVal() { |
| | | return ordinal(); |
| | | } |
| | | |
| | | @JsonCreator |
| | | public static WaylineRcLostActionEnum find(int val) { |
| | | return Arrays.stream(values()).filter(lostActionEnum -> lostActionEnum.ordinal() == val).findAny().get(); |
| | | } |
| | | } |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/10/27 |
| | | */ |
| | | @Data |
| | | public class BasicDeviceProperty { |
| | | public abstract class BasicDeviceProperty { |
| | | |
| | | public boolean valid() { |
| | | return false; |
| | |
| | | |
| | | private String deviceType; |
| | | |
| | | private String domainType; |
| | | |
| | | private Integer imminent; |
| | | |
| | | private Integer inTheSky; |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/30 |
| | | */ |
| | | @Data |
| | | public class FirmwareProgressExtReceiver { |
| | | |
| | | private Long rate; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/8 |
| | | */ |
| | | @Data |
| | | public class LiveviewWorldRegionReceiver { |
| | | |
| | | private Double bottom; |
| | | |
| | | private Double left; |
| | | |
| | | private Double right; |
| | | |
| | | private Double top; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.dji.sample.control.model.enums.CameraModeEnum; |
| | | import com.dji.sample.control.model.enums.CameraStateEnum; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/8 |
| | | */ |
| | | @Data |
| | | public class OsdCameraReceiver { |
| | | |
| | | private CameraModeEnum cameraMode; |
| | | |
| | | private LiveviewWorldRegionReceiver liveviewWorldRegion; |
| | | |
| | | private String payloadIndex; |
| | | |
| | | private CameraStateEnum photoState; |
| | | |
| | | private Integer recordTime; |
| | | |
| | | private CameraStateEnum recordingState; |
| | | |
| | | private Long remainPhotoNum; |
| | | |
| | | private Long remainRecordDuration; |
| | | |
| | | private Float zoomFactor; |
| | | |
| | | private Float irZoomFactor; |
| | | } |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.dji.sample.manage.model.enums.DockDrcStateEnum; |
| | | import com.dji.sample.manage.model.enums.DockModeCodeEnum; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | |
| | | |
| | | private StorageReceiver storage; |
| | | |
| | | private Integer modeCode; |
| | | private DockModeCodeEnum modeCode; |
| | | |
| | | private Integer coverState; |
| | | |
| | | private Integer supplementLightState; |
| | | |
| | | private Integer emergencyStopState; |
| | | private Boolean emergencyStopState; |
| | | |
| | | private Integer airConditionerMode; |
| | | |
| | |
| | | private DockSdrReceiver sdr; |
| | | |
| | | private DockWirelessLinkReceiver wirelessLink; |
| | | |
| | | private DockDrcStateEnum drcState; |
| | | } |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.dji.sample.manage.model.enums.DeviceModeCodeEnum; |
| | | import com.dji.sample.manage.model.enums.DroneRcLostActionEnum; |
| | | import com.dji.sample.manage.model.enums.WaylineRcLostActionEnum; |
| | | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
| | | import com.fasterxml.jackson.databind.PropertyNamingStrategy; |
| | | import com.fasterxml.jackson.databind.annotation.JsonNaming; |
| | |
| | | |
| | | private Double longitude; |
| | | |
| | | private Integer modeCode; |
| | | private DeviceModeCodeEnum modeCode; |
| | | |
| | | private Double totalFlightDistance; |
| | | |
| | |
| | | |
| | | private ObstacleAvoidanceReceiver obstacleAvoidance; |
| | | |
| | | private Long activationTime; |
| | | |
| | | private List<OsdCameraReceiver> cameras; |
| | | |
| | | private DroneRcLostActionEnum rcLostAction; |
| | | |
| | | private Integer rthAltitude; |
| | | |
| | | private Integer totalFlightSorties; |
| | | |
| | | private WaylineRcLostActionEnum exitWaylineWhenRcLost; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import com.dji.sample.manage.model.enums.DroneRcLostActionEnum; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.util.Objects; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/3 |
| | | */ |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class OutOfControlActionReceiver extends BasicDeviceProperty { |
| | | |
| | | private Integer value; |
| | | |
| | | @Override |
| | | public boolean valid() { |
| | | return Objects.nonNull(value) && value >= 0 && value < DroneRcLostActionEnum.values().length; |
| | | } |
| | | |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.model.receiver; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.util.Objects; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/3 |
| | | */ |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class RthAltitudeReceiver extends BasicDeviceProperty { |
| | | |
| | | private Integer value; |
| | | |
| | | private static final int RTH_ALTITUDE_MAX = 500; |
| | | |
| | | private static final int RTH_ALTITUDE_MIN = 20; |
| | | |
| | | @Override |
| | | public boolean valid() { |
| | | return Objects.nonNull(this.value) && this.value >= RTH_ALTITUDE_MIN && this.value <= RTH_ALTITUDE_MAX; |
| | | } |
| | | |
| | | } |
| | |
| | | package com.dji.sample.manage.service; |
| | | |
| | | import com.dji.sample.common.model.PaginationData; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicReceiver; |
| | | import com.dji.sample.manage.model.dto.DeviceFirmwareDTO; |
| | | import com.dji.sample.manage.model.dto.DeviceFirmwareNoteDTO; |
| | | import com.dji.sample.manage.model.dto.DeviceFirmwareUpgradeDTO; |
| | | import com.dji.sample.manage.model.param.DeviceFirmwareQueryParam; |
| | | import com.dji.sample.manage.model.param.DeviceFirmwareUploadParam; |
| | | import com.dji.sample.manage.model.param.DeviceOtaCreateParam; |
| | | import org.springframework.messaging.MessageHeaders; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import java.util.List; |
| | |
| | | * @return |
| | | */ |
| | | List<DeviceOtaCreateParam> getDeviceOtaFirmware(String workspaceId, List<DeviceFirmwareUpgradeDTO> upgradeDTOS); |
| | | |
| | | /** |
| | | * Interface to handle device firmware update progress. |
| | | * @param receiver |
| | | * @param headers |
| | | */ |
| | | void handleOtaProgress(CommonTopicReceiver receiver, MessageHeaders headers); |
| | | |
| | | /** |
| | | * Query firmware version information by page. |
| | |
| | | * Handle logs file upload progress. |
| | | * @param receiver |
| | | * @param headers |
| | | * @return |
| | | */ |
| | | void handleFileUploadProgress(CommonTopicReceiver receiver, MessageHeaders headers); |
| | | CommonTopicReceiver handleFileUploadProgress(CommonTopicReceiver receiver, MessageHeaders headers); |
| | | |
| | | /** |
| | | * Update status, which is updated when the logs upload succeeds or fails. |
| | |
| | | void updateFirmwareVersion(FirmwareVersionReceiver receiver); |
| | | |
| | | /** |
| | | * Handle the topic that contains the payloads field in the state, and save the payloads data. |
| | | * @param payloadReceiverList |
| | | * @param timestamp |
| | | */ |
| | | void saveDeviceBasicPayload(List<DevicePayloadReceiver> payloadReceiverList, Long timestamp); |
| | | |
| | | /** |
| | | * Delete payload data based on payload sn. |
| | | * @param payloadSns |
| | | */ |
| | | void deletePayloadsByPayloadsSn(Collection<String> payloadSns); |
| | | |
| | | /** |
| | | * Check if the device has payload control. |
| | | * @param deviceSn |
| | | * @param payloadIndex |
| | | * @return |
| | | */ |
| | | Boolean checkAuthorityPayload(String deviceSn, String payloadIndex); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.manage.service; |
| | | |
| | | import com.dji.sample.component.mqtt.model.EventsOutputProgressReceiver; |
| | | import com.dji.sample.component.mqtt.model.EventsReceiver; |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.model.receiver.FirmwareProgressExtReceiver; |
| | | |
| | | import java.util.Optional; |
| | | import java.util.Set; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/21 |
| | | */ |
| | | public interface IDeviceRedisService { |
| | | |
| | | /** |
| | | * Determine if the device is online. |
| | | * @param sn |
| | | * @return |
| | | */ |
| | | Boolean checkDeviceOnline(String sn); |
| | | |
| | | /** |
| | | * Query the basic information of the device in redis. |
| | | * @param sn |
| | | * @return |
| | | */ |
| | | Optional<DeviceDTO> getDeviceOnline(String sn); |
| | | |
| | | /** |
| | | * Save the basic information of the device in redis. |
| | | * @param device |
| | | */ |
| | | void setDeviceOnline(DeviceDTO device); |
| | | |
| | | /** |
| | | * Delete the basic device information saved in redis. |
| | | * @param sn |
| | | * @return |
| | | */ |
| | | Boolean delDeviceOnline(String sn); |
| | | |
| | | /** |
| | | * Get the device's osd real-time data. |
| | | * @param sn |
| | | * @param clazz |
| | | * @param <T> |
| | | * @return |
| | | */ |
| | | <T> Optional<T> getDeviceOsd(String sn, Class<T> clazz); |
| | | |
| | | /** |
| | | * Save the firmware update progress of the device in redis. |
| | | * @param sn |
| | | * @param events |
| | | */ |
| | | void setFirmwareUpgrading(String sn, EventsReceiver<EventsOutputProgressReceiver<FirmwareProgressExtReceiver>> events); |
| | | |
| | | /** |
| | | * Query the firmware update progress of the device in redis. |
| | | * @param sn |
| | | * @return |
| | | */ |
| | | Optional<EventsReceiver<EventsOutputProgressReceiver<FirmwareProgressExtReceiver>>> getFirmwareUpgradingProgress(String sn); |
| | | |
| | | /** |
| | | * Delete the firmware update progress of the device in redis. |
| | | * @param sn |
| | | * @return |
| | | */ |
| | | Boolean delFirmwareUpgrading(String sn); |
| | | |
| | | /** |
| | | * Save the hms key of the device in redis. |
| | | * @param sn |
| | | * @param keys |
| | | */ |
| | | void addEndHmsKeys(String sn, String... keys); |
| | | |
| | | /** |
| | | * Query all hms keys of the device in redis. |
| | | * @param sn |
| | | * @return |
| | | */ |
| | | Set<String> getAllHmsKeys(String sn); |
| | | |
| | | /** |
| | | * Delete all hms keys of the device in redis. |
| | | * @param sn |
| | | * @return |
| | | */ |
| | | Boolean delHmsKeysBySn(String sn); |
| | | } |
| | |
| | | |
| | | import com.dji.sample.common.model.PaginationData; |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicReceiver; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicResponse; |
| | | import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.model.dto.DeviceFirmwareUpgradeDTO; |
| | | import com.dji.sample.manage.model.dto.TopologyDeviceDTO; |
| | | import com.dji.sample.manage.model.enums.DeviceModeCodeEnum; |
| | | import com.dji.sample.manage.model.enums.DeviceSetPropertyEnum; |
| | | import com.dji.sample.manage.model.enums.DockModeCodeEnum; |
| | | import com.dji.sample.manage.model.param.DeviceQueryParam; |
| | | import com.dji.sample.manage.model.receiver.FirmwareVersionReceiver; |
| | | import com.dji.sample.manage.model.receiver.StatusGatewayReceiver; |
| | | import com.fasterxml.jackson.databind.JsonNode; |
| | | import org.springframework.messaging.Message; |
| | | import org.springframework.messaging.MessageHeaders; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.List; |
| | |
| | | Boolean bindDevice(DeviceDTO device); |
| | | |
| | | /** |
| | | * Handle dock binding status requests. |
| | | * Note: If your business does not need to bind the dock to the organization, |
| | | * you can directly reply to the successful message without implementing business logic. |
| | | * @param receiver |
| | | * @param headers |
| | | */ |
| | | void bindStatus(CommonTopicReceiver receiver, MessageHeaders headers); |
| | | |
| | | /** |
| | | * Handle dock binding requests. |
| | | * Note: If your business does not need to bind the dock to the organization, |
| | | * you can directly reply to the successful message without implementing business logic. |
| | | * @param receiver |
| | | * @param headers |
| | | */ |
| | | void bindDevice(CommonTopicReceiver receiver, MessageHeaders headers); |
| | | |
| | | /** |
| | | * Get the binding devices list in one workspace. |
| | | * @param workspaceId |
| | | * @param page |
| | |
| | | * @return device |
| | | */ |
| | | Optional<DeviceDTO> getDeviceBySn(String sn); |
| | | |
| | | /** |
| | | * Update the firmware version information of the device or payload. |
| | | * @param receiver |
| | | */ |
| | | void updateFirmwareVersion(FirmwareVersionReceiver receiver); |
| | | |
| | | /** |
| | | * Create job for device firmware updates. |
| | |
| | | void deviceOnePropertySet(String topic, DeviceSetPropertyEnum propertyEnum, Map.Entry<String, Object> value); |
| | | |
| | | /** |
| | | * Determine if the device is online. |
| | | * @param sn |
| | | * Check the working status of the dock. |
| | | * @param dockSn |
| | | * @return |
| | | */ |
| | | Boolean checkDeviceOnline(String sn); |
| | | DockModeCodeEnum getDockMode(String dockSn); |
| | | |
| | | /** |
| | | * Query the working status of the aircraft. |
| | | * @param deviceSn |
| | | * @return |
| | | */ |
| | | DeviceModeCodeEnum getDeviceMode(String deviceSn); |
| | | |
| | | /** |
| | | * Check if the dock is in drc mode. |
| | | * @param dockSn |
| | | * @return |
| | | */ |
| | | Boolean checkDockDrcMode(String dockSn); |
| | | |
| | | /** |
| | | * Check if the device has flight control. |
| | | * @param gatewaySn |
| | | * @return |
| | | */ |
| | | Boolean checkAuthorityFlight(String gatewaySn); |
| | | |
| | | } |
| | |
| | | |
| | | /** |
| | | * Handle the request for obtaining the organization information corresponding to the device binding. |
| | | * Note: If your business does not need to bind the dock to the organization, |
| | | * you can directly reply to the successful message without implementing business logic. |
| | | * @param receiver |
| | | */ |
| | | void replyOrganizationGet(CommonTopicReceiver receiver, MessageHeaders headers); |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.dji.sample.component.mqtt.model.StateDataEnum; |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import com.dji.sample.component.redis.RedisOpsUtils; |
| | | import com.dji.sample.manage.model.dto.CapacityCameraDTO; |
| | |
| | | |
| | | @Override |
| | | public List<CapacityCameraDTO> getCapacityCameraByDeviceSn(String deviceSn) { |
| | | return (List<CapacityCameraDTO>) RedisOpsUtils.hashGet(StateDataEnum.LIVE_CAPACITY.getDesc(), deviceSn); |
| | | return (List<CapacityCameraDTO>) RedisOpsUtils.hashGet(RedisConst.LIVE_CAPACITY, deviceSn); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean deleteCapacityCameraByDeviceSn(String deviceSn) { |
| | | return RedisOpsUtils.hashDel(StateDataEnum.LIVE_CAPACITY.getDesc(), new String[]{deviceSn}); |
| | | return RedisOpsUtils.hashDel(RedisConst.LIVE_CAPACITY, new String[]{deviceSn}); |
| | | } |
| | | |
| | | @Override |
| | | public void saveCapacityCameraReceiverList(List<CapacityCameraReceiver> capacityCameraReceivers, String deviceSn, Long timestamp) { |
| | | List<CapacityCameraDTO> capacity = capacityCameraReceivers.stream() |
| | | .map(this::receiver2Dto).collect(Collectors.toList()); |
| | | RedisOpsUtils.hashSet(StateDataEnum.LIVE_CAPACITY.getDesc(), deviceSn, capacity); |
| | | RedisOpsUtils.setWithExpire(StateDataEnum.LIVE_CAPACITY + RedisConst.DELIMITER + deviceSn, timestamp, RedisConst.DEVICE_ALIVE_SECOND); |
| | | RedisOpsUtils.hashSet(RedisConst.LIVE_CAPACITY, deviceSn, capacity); |
| | | } |
| | | |
| | | @Override |
| | |
| | | import com.dji.sample.component.oss.service.impl.OssServiceContext; |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import com.dji.sample.component.redis.RedisOpsUtils; |
| | | import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | import com.dji.sample.component.websocket.model.BizCodeEnum; |
| | | import com.dji.sample.component.websocket.service.IWebSocketManageService; |
| | | import com.dji.sample.component.websocket.service.impl.SendMessageServiceImpl; |
| | | import com.dji.sample.manage.dao.IDeviceFirmwareMapper; |
| | |
| | | import com.dji.sample.manage.model.param.DeviceFirmwareQueryParam; |
| | | import com.dji.sample.manage.model.param.DeviceFirmwareUploadParam; |
| | | import com.dji.sample.manage.model.param.DeviceOtaCreateParam; |
| | | import com.dji.sample.manage.model.receiver.FirmwareProgressExtReceiver; |
| | | import com.dji.sample.manage.service.IDeviceFirmwareService; |
| | | import com.dji.sample.manage.service.IDeviceService; |
| | | import com.dji.sample.manage.service.IDeviceRedisService; |
| | | import com.dji.sample.manage.service.IFirmwareModelService; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.integration.annotation.ServiceActivator; |
| | | import org.springframework.integration.mqtt.support.MqttHeaders; |
| | | import org.springframework.messaging.MessageHeaders; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.DigestUtils; |
| | |
| | | private IWebSocketManageService webSocketManageService; |
| | | |
| | | @Autowired |
| | | private IDeviceService deviceService; |
| | | |
| | | @Autowired |
| | | private OssServiceContext ossServiceContext; |
| | | |
| | | @Autowired |
| | | private IFirmwareModelService firmwareModelService; |
| | | |
| | | @Autowired |
| | | private IDeviceRedisService deviceRedisService; |
| | | |
| | | @Override |
| | | public Optional<DeviceFirmwareDTO> getFirmware(String workspaceId, String deviceName, String version) { |
| | |
| | | public List<DeviceOtaCreateParam> getDeviceOtaFirmware(String workspaceId, List<DeviceFirmwareUpgradeDTO> upgradeDTOS) { |
| | | List<DeviceOtaCreateParam> deviceOtaList = new ArrayList<>(); |
| | | upgradeDTOS.forEach(upgradeDevice -> { |
| | | boolean exist = deviceService.checkDeviceOnline(upgradeDevice.getSn()); |
| | | boolean exist = deviceRedisService.checkDeviceOnline(upgradeDevice.getSn()); |
| | | if (!exist) { |
| | | throw new IllegalArgumentException("Device is offline."); |
| | | } |
| | |
| | | return deviceOtaList; |
| | | } |
| | | |
| | | @Override |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_OTA_PROGRESS, outputChannel = ChannelName.OUTBOUND) |
| | | public void handleOtaProgress(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString(); |
| | | String sn = topic.substring((TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT).length(), |
| | | topic.indexOf(TopicConst.EVENTS_SUF)); |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_OTA_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) |
| | | public CommonTopicReceiver handleOtaProgress(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | String sn = receiver.getGateway(); |
| | | |
| | | EventsReceiver<EventsOutputReceiver> eventsReceiver = objectMapper.convertValue(receiver.getData(), |
| | | new TypeReference<EventsReceiver<EventsOutputReceiver>>(){}); |
| | | EventsReceiver<EventsOutputProgressReceiver<FirmwareProgressExtReceiver>> eventsReceiver = objectMapper.convertValue(receiver.getData(), |
| | | new TypeReference<EventsReceiver<EventsOutputProgressReceiver<FirmwareProgressExtReceiver>>>(){}); |
| | | eventsReceiver.setBid(receiver.getBid()); |
| | | |
| | | EventsOutputReceiver output = eventsReceiver.getOutput(); |
| | | EventsOutputProgressReceiver<FirmwareProgressExtReceiver> output = eventsReceiver.getOutput(); |
| | | log.info("SN: {}, {} ===> Upgrading progress: {}", |
| | | sn, receiver.getMethod(), output.getProgress().toString()); |
| | | |
| | |
| | | log.error("SN: {}, {} ===> Error code: {}", sn, receiver.getMethod(), eventsReceiver.getResult()); |
| | | } |
| | | |
| | | DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + sn); |
| | | String childDeviceSn = device.getChildDeviceSn(); |
| | | boolean upgrade = RedisOpsUtils.getExpire(RedisConst.FIRMWARE_UPGRADING_PREFIX + sn) > 0; |
| | | boolean childUpgrade = RedisOpsUtils.getExpire(RedisConst.FIRMWARE_UPGRADING_PREFIX + childDeviceSn) > 0; |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(sn); |
| | | if (deviceOpt.isEmpty()) { |
| | | return null; |
| | | } |
| | | |
| | | // Determine whether it is the ending state, delete the update state key in redis after the job ends. |
| | | EventsResultStatusEnum statusEnum = EventsResultStatusEnum.find(output.getStatus()); |
| | | Collection<ConcurrentWebSocketSession> sessions = webSocketManageService.getValueWithWorkspaceAndUserType( |
| | | device.getWorkspaceId(), UserTypeEnum.WEB.getVal()); |
| | | CustomWebSocketMessage<Object> build = CustomWebSocketMessage.builder() |
| | | .data(eventsReceiver) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(receiver.getMethod()) |
| | | .build(); |
| | | if (upgrade) { |
| | | if (statusEnum.getEnd()) { |
| | | DeviceDTO device = deviceOpt.get(); |
| | | handleProgress(device.getWorkspaceId(), sn, eventsReceiver, statusEnum.getEnd()); |
| | | handleProgress(device.getWorkspaceId(), device.getChildDeviceSn(), eventsReceiver, statusEnum.getEnd()); |
| | | |
| | | return receiver; |
| | | } |
| | | |
| | | private void handleProgress(String workspaceId, String sn, |
| | | EventsReceiver<EventsOutputProgressReceiver<FirmwareProgressExtReceiver>> events, boolean isEnd) { |
| | | boolean upgrade = deviceRedisService.getFirmwareUpgradingProgress(sn).isPresent(); |
| | | if (!upgrade) { |
| | | return; |
| | | } |
| | | if (isEnd) { |
| | | // Delete the cache after the update is complete. |
| | | RedisOpsUtils.del(RedisConst.FIRMWARE_UPGRADING_PREFIX + sn); |
| | | deviceRedisService.delFirmwareUpgrading(sn); |
| | | } else { |
| | | // Update the update progress of the dock in redis. |
| | | RedisOpsUtils.setWithExpire( |
| | | RedisConst.FIRMWARE_UPGRADING_PREFIX + sn, output.getProgress().getPercent(), |
| | | RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND); |
| | | deviceRedisService.setFirmwareUpgrading(sn, events); |
| | | } |
| | | eventsReceiver.setSn(sn); |
| | | webSocketMessageService.sendBatch(sessions, build); |
| | | } |
| | | if (childUpgrade) { |
| | | if (!StringUtils.hasText(eventsReceiver.getSn())) { |
| | | eventsReceiver.setSn(childDeviceSn); |
| | | webSocketMessageService.sendBatch(sessions, build); |
| | | } |
| | | if (statusEnum.getEnd()) { |
| | | RedisOpsUtils.del(RedisConst.FIRMWARE_UPGRADING_PREFIX + childDeviceSn); |
| | | } else { |
| | | // Update the update progress of the drone in redis. |
| | | RedisOpsUtils.setWithExpire( |
| | | RedisConst.FIRMWARE_UPGRADING_PREFIX + childDeviceSn, output.getProgress().getPercent(), |
| | | RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND); |
| | | } |
| | | } |
| | | |
| | | if (receiver.getNeedReply() != null && receiver.getNeedReply() == 1) { |
| | | String replyTopic = headers.get(MqttHeaders.RECEIVED_TOPIC) + TopicConst._REPLY_SUF; |
| | | messageSenderService.publish(replyTopic, |
| | | CommonTopicResponse.builder() |
| | | .tid(receiver.getTid()) |
| | | .bid(receiver.getBid()) |
| | | .method(receiver.getMethod()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data(RequestsReply.success()) |
| | | .build()); |
| | | } |
| | | events.setSn(sn); |
| | | webSocketMessageService.sendBatch(workspaceId, UserTypeEnum.WEB.getVal(), BizCodeEnum.OTA_PROGRESS.getCode(), events); |
| | | } |
| | | |
| | | @Override |
| | |
| | | import com.dji.sample.component.mqtt.model.CommonTopicReceiver; |
| | | import com.dji.sample.component.mqtt.model.MapKeyConst; |
| | | import com.dji.sample.component.mqtt.model.TopicConst; |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import com.dji.sample.component.redis.RedisOpsUtils; |
| | | import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; |
| | | import com.dji.sample.component.websocket.model.BizCodeEnum; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | import com.dji.sample.component.websocket.service.impl.SendMessageServiceImpl; |
| | | import com.dji.sample.component.websocket.service.impl.WebSocketManageServiceImpl; |
| | | import com.dji.sample.manage.dao.IDeviceHmsMapper; |
| | |
| | | import com.dji.sample.manage.model.dto.DeviceHmsDTO; |
| | | import com.dji.sample.manage.model.dto.TelemetryDTO; |
| | | import com.dji.sample.manage.model.entity.DeviceHmsEntity; |
| | | import com.dji.sample.manage.model.enums.DeviceDomainEnum; |
| | | import com.dji.sample.manage.model.enums.HmsEnum; |
| | | import com.dji.sample.manage.model.enums.UserTypeEnum; |
| | | import com.dji.sample.manage.model.param.DeviceHmsQueryParam; |
| | | import com.dji.sample.manage.model.receiver.DeviceHmsReceiver; |
| | | import com.dji.sample.manage.model.receiver.HmsArgsReceiver; |
| | | import com.dji.sample.manage.service.IDeviceHmsService; |
| | | import com.dji.sample.manage.service.IDeviceRedisService; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.integration.annotation.ServiceActivator; |
| | | import org.springframework.integration.mqtt.support.MqttHeaders; |
| | |
| | | */ |
| | | @Service |
| | | @Transactional |
| | | @Slf4j |
| | | public class DeviceHmsServiceImpl implements IDeviceHmsService { |
| | | |
| | | @Autowired |
| | |
| | | |
| | | @Autowired |
| | | private WebSocketManageServiceImpl webSocketManageService; |
| | | |
| | | @Autowired |
| | | private IDeviceRedisService deviceRedisService; |
| | | |
| | | private static final Pattern PATTERN_KEY = Pattern.compile( |
| | | HmsEnum.FormatKeyEnum.KEY_START + |
| | |
| | | .updateTime(0L) |
| | | .sn(sn) |
| | | .build(); |
| | | String key = RedisConst.HMS_PREFIX + sn; |
| | | // Query all unread hms messages of the device in redis. |
| | | Set<String> hmsMap = RedisOpsUtils.listGetAll(key).stream().map(String::valueOf).collect(Collectors.toSet()); |
| | | |
| | | DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + sn); |
| | | Set<String> hmsMap = deviceRedisService.getAllHmsKeys(sn); |
| | | |
| | | List<DeviceHmsDTO> unReadList = new ArrayList<>(); |
| | | objectMapper.convertValue(((Map) (receiver.getData())).get(MapKeyConst.LIST), |
| | |
| | | if (unReadList.isEmpty()) { |
| | | return; |
| | | } |
| | | RedisOpsUtils.listRPush(key, unReadList.stream().map(DeviceHmsDTO::getKey).toArray(String[]::new)); |
| | | deviceRedisService.addEndHmsKeys(sn, unReadList.stream().map(DeviceHmsDTO::getKey).toArray(String[]::new)); |
| | | // push to the web |
| | | Collection<ConcurrentWebSocketSession> sessions = webSocketManageService.getValueWithWorkspaceAndUserType( |
| | | device.getWorkspaceId(), UserTypeEnum.WEB.getVal()); |
| | | sendMessageService.sendBatch(sessions, CustomWebSocketMessage.builder() |
| | | .bizCode(BizCodeEnum.DEVICE_HMS.getCode()) |
| | | .data(TelemetryDTO.<List<DeviceHmsDTO>>builder().sn(sn).host(unReadList).build()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .build()); |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(sn); |
| | | if (deviceOpt.isEmpty()) { |
| | | return; |
| | | } |
| | | sendMessageService.sendBatch(deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), |
| | | BizCodeEnum.DEVICE_HMS.getCode(), TelemetryDTO.<List<DeviceHmsDTO>>builder().sn(sn).host(unReadList).build()); |
| | | } |
| | | |
| | | @Override |
| | |
| | | .eq(DeviceHmsEntity::getSn, deviceSn) |
| | | .eq(DeviceHmsEntity::getUpdateTime, 0L)); |
| | | // Delete unread messages cached in redis. |
| | | RedisOpsUtils.del(RedisConst.HMS_PREFIX + deviceSn); |
| | | deviceRedisService.delHmsKeysBySn(deviceSn); |
| | | } |
| | | |
| | | private DeviceHmsDTO entity2Dto(DeviceHmsEntity entity) { |
| | |
| | | dto.setLevel(receiver.getLevel()); |
| | | dto.setModule(receiver.getModule()); |
| | | dto.setHmsId(UUID.randomUUID().toString()); |
| | | |
| | | if (HmsEnum.DomainType.DRONE_NEST.getDomain().equals(receiver.getDomainType())) { |
| | | Optional<DeviceDomainEnum> domainEnumOpt = Optional.ofNullable(receiver.getDeviceType()) |
| | | .map(type -> type.split("-")).map(type -> type[0]).map(Integer::parseInt).map(DeviceDomainEnum::find); |
| | | if (domainEnumOpt.isEmpty()) { |
| | | throw new RuntimeException("The device type does not match, please check the data."); |
| | | } |
| | | if (DeviceDomainEnum.DOCK == domainEnumOpt.get()) { |
| | | dto.setHmsKey(HmsEnum.HmsFaqIdEnum.DOCK_TIP.getText() + receiver.getCode()); |
| | | return; |
| | | } |
| | |
| | | import com.dji.sample.component.mqtt.service.IMessageSenderService; |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import com.dji.sample.component.redis.RedisOpsUtils; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | import com.dji.sample.component.websocket.model.BizCodeEnum; |
| | | import com.dji.sample.component.websocket.service.ISendMessageService; |
| | | import com.dji.sample.component.websocket.service.IWebSocketManageService; |
| | | import com.dji.sample.manage.dao.IDeviceLogsMapper; |
| | | import com.dji.sample.manage.model.dto.*; |
| | | import com.dji.sample.manage.model.entity.DeviceLogsEntity; |
| | |
| | | import com.dji.sample.manage.model.param.LogsFileUpdateParam; |
| | | import com.dji.sample.manage.model.receiver.*; |
| | | import com.dji.sample.manage.service.IDeviceLogsService; |
| | | import com.dji.sample.manage.service.IDeviceRedisService; |
| | | import com.dji.sample.manage.service.ILogsFileService; |
| | | import com.dji.sample.manage.service.ITopologyService; |
| | | import com.dji.sample.media.model.StsCredentialsDTO; |
| | |
| | | private ILogsFileService logsFileService; |
| | | |
| | | @Autowired |
| | | private RedisOpsUtils redisOpsUtils; |
| | | |
| | | @Autowired |
| | | private IStorageService storageService; |
| | | |
| | | @Autowired |
| | |
| | | private ISendMessageService webSocketMessageService; |
| | | |
| | | @Autowired |
| | | private IWebSocketManageService webSocketManageService; |
| | | private IDeviceRedisService deviceRedisService; |
| | | |
| | | @Override |
| | | public PaginationData<DeviceLogsDTO> getUploadedLogs(String deviceSn, DeviceLogsQueryParam param) { |
| | |
| | | |
| | | @Override |
| | | public ResponseResult getRealTimeLogs(String deviceSn, List<String> domainList) { |
| | | boolean exist = redisOpsUtils.getExpire(RedisConst.DEVICE_ONLINE_PREFIX + deviceSn) > 0; |
| | | boolean exist = deviceRedisService.checkDeviceOnline(deviceSn); |
| | | if (!exist) { |
| | | return ResponseResult.error("Device is offline."); |
| | | } |
| | | |
| | | String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + deviceSn + TopicConst.SERVICES_SUF; |
| | | LogsFileUploadList data = messageSenderService.publishWithReply( |
| | | LogsFileUploadList.class, |
| | | topic, |
| | | CommonTopicResponse.builder() |
| | | .tid(UUID.randomUUID().toString()) |
| | | .bid(UUID.randomUUID().toString()) |
| | | .method(LogsFileMethodEnum.FILE_UPLOAD_LIST.getMethod()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data(Map.of(MapKeyConst.MODULE_LIST, domainList)) |
| | | .build(), 1); |
| | | ServiceReply<List<LogsFileUpload>> data = messageSenderService.publishServicesTopic( |
| | | new TypeReference<List<LogsFileUpload>>() {}, deviceSn, LogsFileMethodEnum.FILE_UPLOAD_LIST.getMethod(), |
| | | Map.of(MapKeyConst.MODULE_LIST, domainList)); |
| | | |
| | | for (LogsFileUpload file : data.getFiles()) { |
| | | for (LogsFileUpload file : data.getOutput()) { |
| | | if (file.getDeviceSn().isBlank()) { |
| | | file.setDeviceSn(deviceSn); |
| | | } |
| | | } |
| | | return ResponseResult.success(data); |
| | | return ResponseResult.success(new LogsFileUploadList(data.getOutput(), data.getResult())); |
| | | } |
| | | |
| | | @Override |
| | |
| | | |
| | | credentialsDTO.setParams(LogsFileUploadList.builder().files(files).build()); |
| | | String bid = UUID.randomUUID().toString(); |
| | | ServiceReply reply = messageSenderService.publishWithReply( |
| | | TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + deviceSn + TopicConst.SERVICES_SUF, |
| | | CommonTopicResponse.<LogsUploadCredentialsDTO>builder() |
| | | .tid(UUID.randomUUID().toString()) |
| | | .bid(bid) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .method(LogsFileMethodEnum.FILE_UPLOAD_START.getMethod()) |
| | | .data(credentialsDTO) |
| | | .build()); |
| | | ServiceReply reply = messageSenderService.publishServicesTopic( |
| | | deviceSn, LogsFileMethodEnum.FILE_UPLOAD_START.getMethod(), credentialsDTO, bid); |
| | | |
| | | if (ResponseResult.CODE_SUCCESS != reply.getResult()) { |
| | | return ResponseResult.error(String.valueOf(reply.getResult())); |
| | |
| | | } |
| | | |
| | | // Save the status of the log upload. |
| | | redisOpsUtils.hashSet(RedisConst.LOGS_FILE_PREFIX + deviceSn, bid, LogsOutputProgressDTO.builder().logsId(logsId).build()); |
| | | RedisOpsUtils.hashSet(RedisConst.LOGS_FILE_PREFIX + deviceSn, bid, LogsOutputProgressDTO.builder().logsId(logsId).build()); |
| | | return ResponseResult.success(); |
| | | |
| | | } |
| | |
| | | if (LogsFileUpdateMethodEnum.UNKNOWN == method) { |
| | | return ResponseResult.error("Illegal param"); |
| | | } |
| | | String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + deviceSn + TopicConst.SERVICES_SUF; |
| | | String bid = UUID.randomUUID().toString(); |
| | | ServiceReply reply = messageSenderService.publishWithReply(topic, |
| | | CommonTopicResponse.<LogsFileUpdateParam>builder() |
| | | .tid(UUID.randomUUID().toString()) |
| | | .bid(bid) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .method(LogsFileMethodEnum.FILE_UPLOAD_UPDATE.getMethod()) |
| | | .data(param) |
| | | .build()); |
| | | ServiceReply reply = messageSenderService.publishServicesTopic( |
| | | deviceSn, LogsFileMethodEnum.FILE_UPLOAD_UPDATE.getMethod(), param); |
| | | |
| | | if (ResponseResult.CODE_SUCCESS != reply.getResult()) { |
| | | return ResponseResult.error("Error Code : " + reply.getResult()); |
| | |
| | | logsFileService.deleteFileByLogsId(logsId); |
| | | } |
| | | |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FILE_UPLOAD_PROGRESS, outputChannel = ChannelName.OUTBOUND) |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FILE_UPLOAD_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) |
| | | @Override |
| | | public void handleFileUploadProgress(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | public CommonTopicReceiver handleFileUploadProgress(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString(); |
| | | String sn = topic.substring((TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT).length(), |
| | | topic.indexOf(TopicConst.EVENTS_SUF)); |
| | | |
| | | if (receiver.getNeedReply() != null && receiver.getNeedReply() == 1) { |
| | | String replyTopic = headers.get(MqttHeaders.RECEIVED_TOPIC) + TopicConst._REPLY_SUF; |
| | | messageSenderService.publish(replyTopic, |
| | | CommonTopicResponse.builder() |
| | | .tid(receiver.getTid()) |
| | | .bid(receiver.getBid()) |
| | | .method(receiver.getMethod()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data(RequestsReply.success()) |
| | | .build()); |
| | | } |
| | | |
| | | EventsReceiver<OutputLogsProgressReceiver> eventsReceiver = objectMapper.convertValue(receiver.getData(), |
| | | new TypeReference<EventsReceiver<OutputLogsProgressReceiver>>(){}); |
| | |
| | | webSocketData.setBid(receiver.getBid()); |
| | | webSocketData.setSn(sn); |
| | | |
| | | DeviceDTO device = (DeviceDTO) redisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + sn); |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(sn); |
| | | if (deviceOpt.isEmpty()) { |
| | | return null; |
| | | } |
| | | |
| | | DeviceDTO device = deviceOpt.get(); |
| | | |
| | | try { |
| | | OutputLogsProgressReceiver output = eventsReceiver.getOutput(); |
| | |
| | | |
| | | String key = RedisConst.LOGS_FILE_PREFIX + sn; |
| | | LogsOutputProgressDTO progress; |
| | | boolean exist = redisOpsUtils.checkExist(key); |
| | | boolean exist = RedisOpsUtils.checkExist(key); |
| | | if (!exist && !statusEnum.getEnd()) { |
| | | progress = LogsOutputProgressDTO.builder().logsId(receiver.getBid()).build(); |
| | | redisOpsUtils.hashSet(key, receiver.getBid(), progress); |
| | | RedisOpsUtils.hashSet(key, receiver.getBid(), progress); |
| | | } else if (exist) { |
| | | progress = (LogsOutputProgressDTO) redisOpsUtils.hashGet(key, receiver.getBid()); |
| | | progress = (LogsOutputProgressDTO) RedisOpsUtils.hashGet(key, receiver.getBid()); |
| | | } else { |
| | | progress = LogsOutputProgressDTO.builder().build(); |
| | | } |
| | |
| | | // If the logs file is empty, delete the cache of this task. |
| | | List<LogsExtFileReceiver> fileReceivers = output.getExt().getFiles(); |
| | | if (CollectionUtils.isEmpty(fileReceivers)) { |
| | | redisOpsUtils.del(RedisConst.LOGS_FILE_PREFIX + sn); |
| | | RedisOpsUtils.del(RedisConst.LOGS_FILE_PREFIX + sn); |
| | | } |
| | | |
| | | // refresh cache. |
| | |
| | | }); |
| | | progress.setFiles(fileProgressList); |
| | | webSocketData.setOutput(progress); |
| | | redisOpsUtils.hashSet(RedisConst.LOGS_FILE_PREFIX + sn, receiver.getBid(), progress); |
| | | RedisOpsUtils.hashSet(RedisConst.LOGS_FILE_PREFIX + sn, receiver.getBid(), progress); |
| | | // Delete the cache at the end of the task. |
| | | if (statusEnum.getEnd()) { |
| | | redisOpsUtils.del(RedisConst.LOGS_FILE_PREFIX + sn); |
| | | RedisOpsUtils.del(RedisConst.LOGS_FILE_PREFIX + sn); |
| | | this.updateLogsStatus(receiver.getBid(), DeviceLogsStatusEnum.find(statusEnum).getVal()); |
| | | |
| | | fileReceivers.forEach(file -> logsFileService.updateFile(receiver.getBid(), file)); |
| | |
| | | } catch (NullPointerException e) { |
| | | this.updateLogsStatus(receiver.getBid(), DeviceLogsStatusEnum.FAILED.getVal()); |
| | | |
| | | redisOpsUtils.del(RedisConst.LOGS_FILE_PREFIX + sn); |
| | | RedisOpsUtils.del(RedisConst.LOGS_FILE_PREFIX + sn); |
| | | } |
| | | |
| | | webSocketMessageService.sendBatch( |
| | | webSocketManageService.getValueWithWorkspaceAndUserType( |
| | | device.getWorkspaceId(), UserTypeEnum.WEB.getVal()), |
| | | CustomWebSocketMessage.builder() |
| | | .data(webSocketData) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(receiver.getMethod()) |
| | | .build()); |
| | | webSocketMessageService.sendBatch(device.getWorkspaceId(), UserTypeEnum.WEB.getVal(), |
| | | BizCodeEnum.FILE_UPLOAD_PROGRESS.getCode(), webSocketData); |
| | | |
| | | return receiver; |
| | | } |
| | | |
| | | @Override |
| | |
| | | return null; |
| | | } |
| | | String key = RedisConst.LOGS_FILE_PREFIX + entity.getDeviceSn(); |
| | | LogsOutputProgressDTO progress = new LogsOutputProgressDTO(); |
| | | if (redisOpsUtils.hashCheck(key, entity.getLogsId())) { |
| | | progress = (LogsOutputProgressDTO) redisOpsUtils.hashGet(key, entity.getLogsId()); |
| | | LogsOutputProgressDTO progress = null; |
| | | if (RedisOpsUtils.hashCheck(key, entity.getLogsId())) { |
| | | progress = (LogsOutputProgressDTO) RedisOpsUtils.hashGet(key, entity.getLogsId()); |
| | | } |
| | | |
| | | return DeviceLogsDTO.builder() |
| | |
| | | .logsInformation(entity.getLogsInfo()) |
| | | .userName(entity.getUsername()) |
| | | .deviceLogs(LogsFileUploadList.builder().files(logsFileService.getLogsFileByLogsId(entity.getLogsId())).build()) |
| | | .logsProgress(progress.getFiles()) |
| | | .logsProgress(Objects.requireNonNullElse(progress, new LogsOutputProgressDTO()).getFiles()) |
| | | .deviceTopo(topologyService.getDeviceTopologyByGatewaySn(entity.getDeviceSn()).orElse(null)) |
| | | .build(); |
| | | } |
| | |
| | | Map<String, Object> receiverData = (Map<String, Object>) receiver.getData(); |
| | | data.setPayloads(payloadsList.stream() |
| | | .map(payload -> mapper.convertValue( |
| | | receiverData.getOrDefault(payload.getPayloadName(), Map.of()), |
| | | receiverData.getOrDefault(payload.getPayloadIndex(), Map.of()), |
| | | OsdPayloadReceiver.class)) |
| | | .collect(Collectors.toList())); |
| | | |
| | |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.dji.sample.component.mqtt.model.ChannelName; |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import com.dji.sample.component.redis.RedisOpsUtils; |
| | | import com.dji.sample.component.websocket.model.BizCodeEnum; |
| | | import com.dji.sample.component.websocket.service.ISendMessageService; |
| | | import com.dji.sample.control.model.enums.DroneAuthorityEnum; |
| | | import com.dji.sample.manage.dao.IDevicePayloadMapper; |
| | | import com.dji.sample.manage.model.dto.DeviceAuthorityDTO; |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.model.dto.DeviceDictionaryDTO; |
| | | import com.dji.sample.manage.model.dto.DevicePayloadDTO; |
| | | import com.dji.sample.manage.model.entity.DevicePayloadEntity; |
| | | import com.dji.sample.manage.model.enums.ControlSourceEnum; |
| | | import com.dji.sample.manage.model.enums.DeviceDomainEnum; |
| | | import com.dji.sample.manage.model.enums.UserTypeEnum; |
| | | import com.dji.sample.manage.model.receiver.DevicePayloadReceiver; |
| | | import com.dji.sample.manage.model.receiver.FirmwareVersionReceiver; |
| | | import com.dji.sample.manage.service.ICapacityCameraService; |
| | | import com.dji.sample.manage.service.IDeviceDictionaryService; |
| | | import com.dji.sample.manage.service.IDevicePayloadService; |
| | | import com.dji.sample.manage.service.IDeviceRedisService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.integration.annotation.ServiceActivator; |
| | | import org.springframework.messaging.MessageHeaders; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.CollectionUtils; |
| | |
| | | @Autowired |
| | | private ICapacityCameraService capacityCameraService; |
| | | |
| | | @Autowired |
| | | private ISendMessageService sendMessageService; |
| | | |
| | | @Autowired |
| | | private IDeviceRedisService deviceRedisService; |
| | | |
| | | @Override |
| | | public Integer checkPayloadExist(String payloadSn) { |
| | | DevicePayloadEntity devicePayload = mapper.selectOne( |
| | |
| | | // If it already exists, update the data directly. |
| | | if (id > 0) { |
| | | entity.setId(id); |
| | | return mapper.updateById(entity); |
| | | // For the payload of the drone itself, there is no firmware version. |
| | | entity.setFirmwareVersion(null); |
| | | return mapper.updateById(entity) > 0 ? entity.getId() : 0; |
| | | } |
| | | return mapper.insert(entity) > 0 ? entity.getId() : 0; |
| | | } |
| | |
| | | } |
| | | |
| | | String deviceSn = payloadReceiverList.get(0).getDeviceSn(); |
| | | String key = RedisConst.DEVICE_ONLINE_PREFIX + deviceSn; |
| | | DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(key); |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(deviceSn); |
| | | if (deviceOpt.isEmpty()) { |
| | | return false; |
| | | } |
| | | DeviceDTO device = deviceOpt.get(); |
| | | List<DevicePayloadDTO> payloads = new ArrayList<>(); |
| | | |
| | | Map<String, String> controlMap = CollectionUtils.isEmpty(device.getPayloadsList()) ? |
| | | Collections.emptyMap() : |
| | | device.getPayloadsList().stream() |
| | | .collect(Collectors.toMap(DevicePayloadDTO::getPayloadIndex, DevicePayloadDTO::getControlSource)); |
| | | |
| | | for (DevicePayloadReceiver payloadReceiver : payloadReceiverList) { |
| | | int payloadId = this.saveOnePayloadDTO(payloadReceiver); |
| | |
| | | return false; |
| | | } |
| | | payloads.add(this.receiver2Dto(payloadReceiver)); |
| | | if (!controlMap.getOrDefault(payloadReceiver.getPayloadIndex(), "").equals(payloadReceiver.getControlSource())) { |
| | | sendMessageService.sendBatch(device.getWorkspaceId(), UserTypeEnum.WEB.getVal(), |
| | | BizCodeEnum.CONTROL_SOURCE_CHANGE.getCode(), |
| | | DeviceAuthorityDTO.builder() |
| | | .controlSource(payloadReceiver.getControlSource()) |
| | | .sn(payloadReceiver.getSn()) |
| | | .type(DroneAuthorityEnum.PAYLOAD) |
| | | .build()); |
| | | } |
| | | } |
| | | |
| | | if (payloads.isEmpty()) { |
| | | payloads = this.getDevicePayloadEntitiesByDeviceSn(deviceSn); |
| | | } |
| | | device.setPayloadsList(payloads); |
| | | RedisOpsUtils.setWithExpire(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn(), device, RedisConst.DEVICE_ALIVE_SECOND); |
| | | |
| | | deviceRedisService.setDeviceOnline(device); |
| | | return true; |
| | | } |
| | | |
| | |
| | | .eq(DevicePayloadEntity::getDeviceSn, receiver.getSn())); |
| | | } |
| | | |
| | | @Override |
| | | public void saveDeviceBasicPayload(List<DevicePayloadReceiver> payloadReceiverList, Long timestamp) { |
| | | /** |
| | | * Handle payload data for devices. |
| | | * @param payloadReceiverList |
| | | * @param headers |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_PAYLOAD) |
| | | public void handleDeviceBasicPayload(List<DevicePayloadReceiver> payloadReceiverList, MessageHeaders headers) { |
| | | if (payloadReceiverList.isEmpty()) { |
| | | return; |
| | | } |
| | | String deviceSn = payloadReceiverList.stream().findAny().get().getDeviceSn(); |
| | | String deviceSn = payloadReceiverList.get(0).getDeviceSn(); |
| | | String key = RedisConst.STATE_PAYLOAD_PREFIX + deviceSn; |
| | | // Solve timing problems |
| | | long last = (long) Objects.requireNonNullElse(RedisOpsUtils.get(key), 0L); |
| | | long timestamp = headers.getTimestamp(); |
| | | if (last > timestamp) { |
| | | return; |
| | | } |
| | | |
| | | |
| | | // Filter unsaved payload information. |
| | | Set<String> payloadSns = this.getDevicePayloadEntitiesByDeviceSn(payloadReceiverList.get(0).getDeviceSn()) |
| | | .stream().map(DevicePayloadDTO::getPayloadSn).collect(Collectors.toSet()); |
| | | |
| | | Set<String> newPayloadSns = payloadReceiverList.stream().map(DevicePayloadReceiver::getSn).collect(Collectors.toSet()); |
| | | Set<String> needToDel = payloadSns.stream().filter(sn -> !newPayloadSns.contains(sn)).collect(Collectors.toSet()); |
| | | this.deletePayloadsByPayloadsSn(needToDel); |
| | | |
| | | List<DevicePayloadReceiver> needToSave = payloadReceiverList.stream() |
| | | .filter(payload -> !payloadSns.contains(payload.getSn())).collect(Collectors.toList()); |
| | | payloadSns.removeAll(newPayloadSns); |
| | | this.deletePayloadsByPayloadsSn(payloadSns); |
| | | |
| | | // Save the new payload information. |
| | | boolean isSave = this.savePayloadDTOs(needToSave); |
| | | boolean isSave = this.savePayloadDTOs(payloadReceiverList); |
| | | if (isSave) { |
| | | RedisOpsUtils.setWithExpire(key, timestamp, RedisConst.DEVICE_ALIVE_SECOND); |
| | | } |
| | |
| | | .or(wrapper -> payloadSns.forEach(sn -> wrapper.eq(DevicePayloadEntity::getPayloadSn, sn)))); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean checkAuthorityPayload(String deviceSn, String payloadIndex) { |
| | | return deviceRedisService.getDeviceOnline(deviceSn).flatMap(device -> |
| | | Optional.of(DeviceDomainEnum.SUB_DEVICE.getVal() == device.getDomain() |
| | | && !CollectionUtils.isEmpty(device.getPayloadsList()) |
| | | && ControlSourceEnum.A.getControlSource() |
| | | .equals(device.getPayloadsList().stream() |
| | | .filter(payload -> payloadIndex.equals(payload.getPayloadIndex())) |
| | | .map(DevicePayloadDTO::getControlSource).findAny() |
| | | .orElse(ControlSourceEnum.B.getControlSource())))).orElse(true); |
| | | |
| | | } |
| | | |
| | | /** |
| | | * Convert database entity objects into payload data transfer object. |
| | | * @param entity |
| | |
| | | builder.payloadSn(entity.getPayloadSn()) |
| | | .payloadName(entity.getPayloadName()) |
| | | .payloadDesc(entity.getPayloadDesc()) |
| | | .payloadIndex(entity.getPayloadIndex()); |
| | | .index(entity.getPayloadIndex()) |
| | | .payloadIndex(entity.getPayloadType() + "-" + entity.getSubType() + "-" + entity.getPayloadIndex()) |
| | | .controlSource(entity.getControlSource()); |
| | | } |
| | | return builder.build(); |
| | | } |
| | |
| | | String[] payloadIndexArr = dto.getPayloadIndex().split("-"); |
| | | try { |
| | | int[] arr = Arrays.stream(payloadIndexArr) |
| | | .map(Integer::valueOf) |
| | | .mapToInt(Integer::intValue) |
| | | .mapToInt(Integer::parseInt) |
| | | .toArray(); |
| | | |
| | | if (arr.length == 3) { |
| | | Optional<DeviceDictionaryDTO> dictionaryOpt = dictionaryService |
| | | .getOneDictionaryInfoByTypeSubType(DeviceDomainEnum.PAYLOAD.getVal(), arr[0], arr[1]); |
| | | dictionaryOpt.ifPresent(dictionary -> |
| | | builder.payloadName(dictionary.getDeviceName()) |
| | | .payloadDesc(dictionary.getDeviceDesc())); |
| | | |
| | | } |
| | | builder.payloadType(arr[0]) |
| | | .subType(arr[1]) |
| | | .payloadIndex(arr[2]); |
| | | .payloadIndex(arr[2]) |
| | | .controlSource(dto.getControlSource()); |
| | | } catch (NumberFormatException e) { |
| | | builder.payloadType(Integer.valueOf(payloadIndexArr[0])) |
| | | builder.payloadType(-1) |
| | | .subType(-1) |
| | | .payloadIndex(Integer.valueOf(payloadIndexArr[2])); |
| | | .payloadIndex(-1); |
| | | } |
| | | |
| | | return builder |
| | |
| | | return builder.build(); |
| | | } |
| | | return builder.payloadSn(receiver.getSn()) |
| | | .payloadName(receiver.getPayloadIndex()) |
| | | .payloadIndex(receiver.getPayloadIndex()) |
| | | .controlSource(receiver.getControlSource()) |
| | | .build(); |
| | | } |
| | | |
| New file |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.dji.sample.component.mqtt.model.EventsOutputProgressReceiver; |
| | | import com.dji.sample.component.mqtt.model.EventsReceiver; |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import com.dji.sample.component.redis.RedisOpsUtils; |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.model.receiver.FirmwareProgressExtReceiver; |
| | | import com.dji.sample.manage.service.IDeviceRedisService; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.Optional; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/21 |
| | | */ |
| | | @Service |
| | | public class DeviceRedisServiceImpl implements IDeviceRedisService { |
| | | |
| | | @Override |
| | | public Boolean checkDeviceOnline(String sn) { |
| | | String key = RedisConst.DEVICE_ONLINE_PREFIX + sn; |
| | | return RedisOpsUtils.checkExist(key) && RedisOpsUtils.getExpire(key) > 0; |
| | | } |
| | | |
| | | @Override |
| | | public Optional<DeviceDTO> getDeviceOnline(String sn) { |
| | | return Optional.ofNullable((DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + sn)); |
| | | } |
| | | |
| | | @Override |
| | | public void setDeviceOnline(DeviceDTO device) { |
| | | RedisOpsUtils.setWithExpire(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn(), device, RedisConst.DEVICE_ALIVE_SECOND); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean delDeviceOnline(String sn) { |
| | | return RedisOpsUtils.del(RedisConst.DEVICE_ONLINE_PREFIX + sn); |
| | | } |
| | | |
| | | @Override |
| | | public <T> Optional<T> getDeviceOsd(String sn, Class<T> clazz) { |
| | | return Optional.ofNullable(clazz.cast(RedisOpsUtils.get(RedisConst.OSD_PREFIX + sn))); |
| | | } |
| | | |
| | | @Override |
| | | public void setFirmwareUpgrading(String sn, EventsReceiver<EventsOutputProgressReceiver<FirmwareProgressExtReceiver>> events) { |
| | | RedisOpsUtils.setWithExpire(RedisConst.FIRMWARE_UPGRADING_PREFIX + sn, events, RedisConst.DEVICE_ALIVE_SECOND * 20); |
| | | } |
| | | |
| | | @Override |
| | | public Optional<EventsReceiver<EventsOutputProgressReceiver<FirmwareProgressExtReceiver>>> getFirmwareUpgradingProgress(String sn) { |
| | | return Optional.ofNullable((EventsReceiver<EventsOutputProgressReceiver<FirmwareProgressExtReceiver>>) RedisOpsUtils.get(RedisConst.FIRMWARE_UPGRADING_PREFIX + sn)); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean delFirmwareUpgrading(String sn) { |
| | | return RedisOpsUtils.del(RedisConst.FIRMWARE_UPGRADING_PREFIX + sn); |
| | | } |
| | | |
| | | @Override |
| | | public void addEndHmsKeys(String sn, String... keys) { |
| | | RedisOpsUtils.listRPush(RedisConst.HMS_PREFIX + sn, keys); |
| | | } |
| | | |
| | | @Override |
| | | public Set<String> getAllHmsKeys(String sn) { |
| | | return RedisOpsUtils.listGetAll(RedisConst.HMS_PREFIX + sn).stream() |
| | | .map(String::valueOf).collect(Collectors.toSet()); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean delHmsKeysBySn(String sn) { |
| | | return RedisOpsUtils.del(RedisConst.HMS_PREFIX + sn); |
| | | } |
| | | } |
| | |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | import com.dji.sample.component.websocket.service.ISendMessageService; |
| | | import com.dji.sample.component.websocket.service.IWebSocketManageService; |
| | | import com.dji.sample.control.model.enums.DroneAuthorityEnum; |
| | | import com.dji.sample.manage.dao.IDeviceMapper; |
| | | import com.dji.sample.manage.model.dto.*; |
| | | import com.dji.sample.manage.model.entity.DeviceEntity; |
| | |
| | | private IDeviceFirmwareService deviceFirmwareService; |
| | | |
| | | @Autowired |
| | | private ICapacityCameraService capacityCameraService; |
| | | |
| | | @Autowired |
| | | private IDeviceRedisService deviceRedisService; |
| | | |
| | | @Autowired |
| | | @Qualifier("gatewayOSDServiceImpl") |
| | | private ITSAService tsaService; |
| | | |
| | |
| | | this.subscribeTopicOnline(gatewaySn); |
| | | |
| | | // Only the remote controller is logged in and the aircraft is not connected. |
| | | String key = RedisConst.DEVICE_ONLINE_PREFIX + gatewaySn; |
| | | |
| | | boolean exist = RedisOpsUtils.checkExist(key); |
| | | if (!exist) { |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(gatewaySn); |
| | | if (deviceOpt.isEmpty()) { |
| | | Optional<DeviceDTO> gatewayOpt = this.getDeviceBySn(gatewaySn); |
| | | if (gatewayOpt.isPresent()) { |
| | | DeviceDTO value = gatewayOpt.get(); |
| | | RedisOpsUtils.setWithExpire(key, value, RedisConst.DEVICE_ALIVE_SECOND); |
| | | value.setChildDeviceSn(null); |
| | | deviceRedisService.setDeviceOnline(value); |
| | | this.pushDeviceOnlineTopo(value.getWorkspaceId(), gatewaySn, gatewaySn); |
| | | return true; |
| | | } |
| | | |
| | | // When connecting for the first time |
| | | DeviceEntity gatewayDevice = deviceGatewayConvertToDeviceEntity(gateway); |
| | | return onlineSaveDevice(gatewayDevice, null).isPresent(); |
| | | return onlineSaveDevice(gatewayDevice, null, null).isPresent(); |
| | | } |
| | | |
| | | DeviceDTO deviceDTO = (DeviceDTO) (RedisOpsUtils.get(key)); |
| | | DeviceDTO deviceDTO = deviceOpt.get(); |
| | | String deviceSn = deviceDTO.getChildDeviceSn(); |
| | | if (!StringUtils.hasText(deviceSn)) { |
| | | return true; |
| | |
| | | public Boolean subDeviceOffline(String deviceSn) { |
| | | |
| | | // If no information about this device exists in the cache, the drone is considered to be offline. |
| | | String key = RedisConst.DEVICE_ONLINE_PREFIX + deviceSn; |
| | | if (!RedisOpsUtils.checkExist(key) || RedisOpsUtils.getExpire(key) <= 0) { |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(deviceSn); |
| | | if (deviceOpt.isEmpty()) { |
| | | log.debug("The drone is already offline."); |
| | | return true; |
| | | } |
| | | DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(key); |
| | | DeviceDTO device = deviceOpt.get(); |
| | | // Cancel drone-related subscriptions. |
| | | this.unsubscribeTopicOffline(deviceSn); |
| | | |
| | | payloadService.deletePayloadsByDeviceSn(new ArrayList<>(List.of(deviceSn))); |
| | | capacityCameraService.deleteCapacityCameraByDeviceSn(deviceSn); |
| | | deviceRedisService.delDeviceOnline(deviceSn); |
| | | RedisOpsUtils.del(RedisConst.OSD_PREFIX + deviceSn); |
| | | deviceRedisService.delHmsKeysBySn(deviceSn); |
| | | // Publish the latest device topology information in the current workspace. |
| | | this.pushDeviceOfflineTopo(device.getWorkspaceId(), deviceSn); |
| | | |
| | | RedisOpsUtils.del(key); |
| | | RedisOpsUtils.del(RedisConst.OSD_PREFIX + device.getDeviceSn()); |
| | | RedisOpsUtils.del(RedisConst.HMS_PREFIX + device.getDeviceSn()); |
| | | log.debug("{} offline.", deviceSn); |
| | | return true; |
| | | } |
| | |
| | | @Override |
| | | public Boolean deviceOnline(StatusGatewayReceiver deviceGateway) { |
| | | String deviceSn = deviceGateway.getSubDevices().get(0).getSn(); |
| | | String key = RedisConst.DEVICE_ONLINE_PREFIX + deviceSn; |
| | | // change log: Use redis instead of |
| | | long time = RedisOpsUtils.getExpire(key); |
| | | long gatewayTime = RedisOpsUtils.getExpire(RedisConst.DEVICE_ONLINE_PREFIX + deviceGateway.getSn()); |
| | | |
| | | if (time > 0 && gatewayTime > 0) { |
| | | RedisOpsUtils.expireKey(key, RedisConst.DEVICE_ALIVE_SECOND); |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(deviceSn); |
| | | Optional<DeviceDTO> gatewayOpt = deviceRedisService.getDeviceOnline(deviceGateway.getSn()); |
| | | |
| | | if (deviceOpt.isPresent() && gatewayOpt.isPresent()) { |
| | | DeviceDTO device = DeviceDTO.builder().loginTime(LocalDateTime.now()).deviceSn(deviceSn).build(); |
| | | DeviceDTO gateway = DeviceDTO.builder() |
| | | .loginTime(LocalDateTime.now()) |
| | |
| | | .childDeviceSn(deviceSn).build(); |
| | | this.updateDevice(gateway); |
| | | this.updateDevice(device); |
| | | String workspaceId = ((DeviceDTO)(RedisOpsUtils.get(key))).getWorkspaceId(); |
| | | String workspaceId = deviceOpt.get().getWorkspaceId(); |
| | | if (StringUtils.hasText(workspaceId)) { |
| | | this.subscribeTopicOnline(deviceSn); |
| | | this.subscribeTopicOnline(deviceGateway.getSn()); |
| | |
| | | }); |
| | | |
| | | DeviceEntity gateway = deviceGatewayConvertToDeviceEntity(deviceGateway); |
| | | Optional<DeviceEntity> gatewayEntityOpt = onlineSaveDevice(gateway, deviceSn); |
| | | Optional<DeviceEntity> gatewayEntityOpt = onlineSaveDevice(gateway, deviceSn, null); |
| | | if (gatewayEntityOpt.isEmpty()) { |
| | | log.error("Failed to go online, please check the status data or code logic."); |
| | | return false; |
| | | } |
| | | |
| | | DeviceEntity subDevice = subDeviceConvertToDeviceEntity(deviceGateway.getSubDevices().get(0)); |
| | | Optional<DeviceEntity> subDeviceEntityOpt = onlineSaveDevice(subDevice, null); |
| | | Optional<DeviceEntity> subDeviceEntityOpt = onlineSaveDevice(subDevice, null, gateway.getDeviceSn()); |
| | | if (subDeviceEntityOpt.isEmpty()) { |
| | | log.error("Failed to go online, please check the status data or code logic."); |
| | | return false; |
| | |
| | | |
| | | devicesList.stream() |
| | | .filter(gateway -> DeviceDomainEnum.DOCK.getVal() == gateway.getDomain() || |
| | | RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + gateway.getDeviceSn())) |
| | | deviceRedisService.checkDeviceOnline(gateway.getDeviceSn())) |
| | | .forEach(this::spliceDeviceTopo); |
| | | |
| | | return devicesList; |
| | |
| | | @Override |
| | | public void spliceDeviceTopo(DeviceDTO gateway) { |
| | | |
| | | gateway.setStatus(RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + gateway.getDeviceSn())); |
| | | gateway.setStatus(deviceRedisService.checkDeviceOnline(gateway.getDeviceSn())); |
| | | |
| | | // sub device |
| | | if (!StringUtils.hasText(gateway.getChildDeviceSn())) { |
| | |
| | | } |
| | | |
| | | DeviceDTO subDevice = getDevicesByParams(DeviceQueryParam.builder().deviceSn(gateway.getChildDeviceSn()).build()).get(0); |
| | | subDevice.setStatus(RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + subDevice.getDeviceSn())); |
| | | subDevice.setStatus(deviceRedisService.checkDeviceOnline(subDevice.getDeviceSn())); |
| | | gateway.setChildren(subDevice); |
| | | |
| | | // payloads |
| | |
| | | |
| | | this.getDeviceTopoForPilot(sn) |
| | | .ifPresent(pilotMessage::setData); |
| | | boolean exist = RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + sn); |
| | | pilotMessage.getData().setOnlineStatus(exist); |
| | | pilotMessage.getData().setOnlineStatus(deviceRedisService.checkDeviceOnline(sn)); |
| | | pilotMessage.getData().setGatewaySn(gatewaySn.equals(sn) ? "" : gatewaySn); |
| | | |
| | | sendMessageService.sendBatch(sessions, pilotMessage); |
| | |
| | | .key(device.getDomain() + "-" + device.getType() + "-" + device.getSubType()) |
| | | .build()) |
| | | .iconUrls(device.getIconUrl()) |
| | | .onlineStatus(RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn())) |
| | | .onlineStatus(deviceRedisService.checkDeviceOnline(device.getDeviceSn())) |
| | | .boundStatus(device.getBoundStatus()) |
| | | .model(device.getDeviceName()) |
| | | .userId(device.getUserId()) |
| | |
| | | String from = topic.substring((THING_MODEL_PRE + PRODUCT).length(), |
| | | topic.indexOf(OSD_SUF)); |
| | | |
| | | // Real-time update of device status in memory |
| | | RedisOpsUtils.expireKey(RedisConst.DEVICE_ONLINE_PREFIX + from, RedisConst.DEVICE_ALIVE_SECOND); |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(from); |
| | | |
| | | DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + from); |
| | | |
| | | if (device == null) { |
| | | Optional<DeviceDTO> deviceOpt = this.getDeviceBySn(from); |
| | | if (deviceOpt.isEmpty()) { |
| | | deviceOpt = this.getDeviceBySn(from); |
| | | if (deviceOpt.isEmpty()) { |
| | | log.error("Please restart the drone."); |
| | | return; |
| | | } |
| | | device = deviceOpt.get(); |
| | | if (!StringUtils.hasText(device.getWorkspaceId())) { |
| | | |
| | | if (!StringUtils.hasText(deviceOpt.get().getWorkspaceId())) { |
| | | this.unsubscribeTopicOffline(from); |
| | | return; |
| | | } |
| | | RedisOpsUtils.setWithExpire(RedisConst.DEVICE_ONLINE_PREFIX + from, device, |
| | | RedisConst.DEVICE_ALIVE_SECOND); |
| | | deviceRedisService.setDeviceOnline(deviceOpt.get()); |
| | | this.subscribeTopicOnline(from); |
| | | } |
| | | DeviceDTO device = deviceOpt.get(); |
| | | deviceRedisService.setDeviceOnline(device); |
| | | |
| | | receiver = objectMapper.readValue(payload, CommonTopicReceiver.class); |
| | | |
| | |
| | | .version(gateway.getVersion()) |
| | | .domain(gateway.getDomain() != null ? |
| | | gateway.getDomain() : DeviceDomainEnum.GATEWAY.getVal()) |
| | | .deviceIndex(gateway.getSubDevices().isEmpty() ? null : gateway.getSubDevices().get(0).getIndex()) |
| | | .build(); |
| | | } |
| | | |
| | |
| | | .deviceType(device.getType()) |
| | | .subType(device.getSubType()) |
| | | .version(device.getVersion()) |
| | | .deviceIndex(device.getIndex()) |
| | | .domain(DeviceDomainEnum.SUB_DEVICE.getVal()) |
| | | .build(); |
| | | } |
| | |
| | | if (entity == null) { |
| | | return null; |
| | | } |
| | | DeviceDTO.DeviceDTOBuilder deviceDTOBuilder = DeviceDTO.builder() |
| | | DeviceDTO deviceDTO = DeviceDTO.builder() |
| | | .deviceSn(entity.getDeviceSn()) |
| | | .childDeviceSn(entity.getChildSn()) |
| | | .deviceName(entity.getDeviceName()) |
| | | .deviceDesc(entity.getDeviceDesc()) |
| | | .deviceIndex(entity.getDeviceIndex()) |
| | | .controlSource(entity.getDeviceIndex()) |
| | | .workspaceId(entity.getWorkspaceId()) |
| | | .type(entity.getDeviceType()) |
| | | .subType(entity.getSubType()) |
| | |
| | | .firmwareVersion(entity.getFirmwareVersion()) |
| | | .workspaceName(entity.getWorkspaceId() != null ? |
| | | workspaceService.getWorkspaceByWorkspaceId(entity.getWorkspaceId()) |
| | | .map(WorkspaceDTO::getWorkspaceName).orElse("") : ""); |
| | | .map(WorkspaceDTO::getWorkspaceName).orElse("") : "") |
| | | .firmwareStatus(DeviceFirmwareStatusEnum.NOT_UPGRADE.getVal()).build(); |
| | | |
| | | addFirmwareStatus(deviceDTO, entity); |
| | | return deviceDTO; |
| | | } |
| | | |
| | | private void addFirmwareStatus(DeviceDTO deviceDTO, DeviceEntity entity) { |
| | | if (!StringUtils.hasText(entity.getFirmwareVersion())) { |
| | | return deviceDTOBuilder.firmwareStatus(DeviceFirmwareStatusEnum.NOT_UPGRADE.getVal()).build(); |
| | | return; |
| | | } |
| | | // Query whether the device is updating firmware. |
| | | Object progress = RedisOpsUtils.get(RedisConst.FIRMWARE_UPGRADING_PREFIX + entity.getDeviceSn()); |
| | | if (Objects.nonNull(progress)) { |
| | | return deviceDTOBuilder.firmwareStatus(DeviceFirmwareStatusEnum.UPGRADING.getVal()).firmwareProgress((int)progress).build(); |
| | | Optional<EventsReceiver<EventsOutputProgressReceiver<FirmwareProgressExtReceiver>>> progressOpt = |
| | | deviceRedisService.getFirmwareUpgradingProgress(entity.getDeviceSn()); |
| | | if (progressOpt.isPresent()) { |
| | | deviceDTO.setFirmwareStatus(DeviceFirmwareStatusEnum.UPGRADING.getVal()); |
| | | deviceDTO.setFirmwareProgress(progressOpt.map(EventsReceiver::getOutput) |
| | | .map(EventsOutputProgressReceiver::getProgress) |
| | | .map(OutputProgressReceiver::getPercent) |
| | | .orElse(0)); |
| | | return; |
| | | } |
| | | |
| | | // First query the latest firmware version of the device model and compare it with the current firmware version |
| | | // to see if it needs to be upgraded. |
| | | Optional<DeviceFirmwareNoteDTO> firmwareReleaseNoteOpt = deviceFirmwareService.getLatestFirmwareReleaseNote(entity.getDeviceName()); |
| | | if (firmwareReleaseNoteOpt.isPresent()) { |
| | | DeviceFirmwareNoteDTO firmwareNoteDTO = firmwareReleaseNoteOpt.get(); |
| | | if (firmwareNoteDTO.getProductVersion().equals(entity.getFirmwareVersion())) { |
| | | return deviceDTOBuilder.firmwareStatus(entity.getCompatibleStatus() ? |
| | | if (firmwareReleaseNoteOpt.isEmpty()) { |
| | | deviceDTO.setFirmwareStatus(DeviceFirmwareStatusEnum.NOT_UPGRADE.getVal()); |
| | | return; |
| | | } |
| | | if (entity.getFirmwareVersion().equals(firmwareReleaseNoteOpt.get().getProductVersion())) { |
| | | deviceDTO.setFirmwareStatus(entity.getCompatibleStatus() ? |
| | | DeviceFirmwareStatusEnum.NOT_UPGRADE.getVal() : |
| | | DeviceFirmwareStatusEnum.CONSISTENT_UPGRADE.getVal()).build(); |
| | | DeviceFirmwareStatusEnum.CONSISTENT_UPGRADE.getVal()); |
| | | return; |
| | | } |
| | | |
| | | return deviceDTOBuilder.firmwareStatus(DeviceFirmwareStatusEnum.NORMAL_UPGRADE.getVal()).build(); |
| | | } |
| | | return deviceDTOBuilder.firmwareStatus(DeviceFirmwareStatusEnum.NOT_UPGRADE.getVal()).build(); |
| | | deviceDTO.setFirmwareStatus(DeviceFirmwareStatusEnum.NORMAL_UPGRADE.getVal()); |
| | | } |
| | | |
| | | @Override |
| | |
| | | return false; |
| | | } |
| | | |
| | | String key = RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn(); |
| | | if (!RedisOpsUtils.checkExist(key)) { |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(device.getDeviceSn()); |
| | | if (deviceOpt.isEmpty()) { |
| | | return false; |
| | | } |
| | | |
| | | DeviceDTO redisDevice = (DeviceDTO)RedisOpsUtils.get(key); |
| | | DeviceDTO redisDevice = deviceOpt.get(); |
| | | redisDevice.setWorkspaceId(device.getWorkspaceId()); |
| | | RedisOpsUtils.setWithExpire(key, redisDevice, RedisConst.DEVICE_ALIVE_SECOND); |
| | | deviceRedisService.setDeviceOnline(redisDevice); |
| | | |
| | | if (DeviceDomainEnum.GATEWAY.getVal() == redisDevice.getDomain()) { |
| | | this.pushDeviceOnlineTopo(webSocketManageService.getValueWithWorkspace(device.getWorkspaceId()), |
| | |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | /** |
| | | * Handle dock binding status requests. |
| | | * @param receiver |
| | | * @param headers |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_AIRPORT_BIND_STATUS, outputChannel = ChannelName.OUTBOUND) |
| | | public void bindStatus(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | List<Map<String, String>> data = ((Map<String, List<Map<String, String>>>) receiver.getData()).get(MapKeyConst.DEVICES); |
| | |
| | | |
| | | } |
| | | |
| | | @Override |
| | | /** |
| | | * Handle dock binding requests. |
| | | * @param receiver |
| | | * @param headers |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_REQUESTS_AIRPORT_ORGANIZATION_BIND) |
| | | public void bindDevice(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | Map<String, List<BindDeviceReceiver>> data = objectMapper.convertValue(receiver.getData(), |
| | |
| | | drone = device; |
| | | } |
| | | } |
| | | |
| | | assert dock != null; |
| | | |
| | | Optional<DeviceEntity> dockEntityOpt = this.bindDevice2Entity(DeviceDomainEnum.DOCK.getVal(), dock); |
| | | Optional<DeviceEntity> droneEntityOpt = this.bindDevice2Entity(DeviceDomainEnum.SUB_DEVICE.getVal(), drone); |
| | |
| | | .eq(DeviceEntity::getBoundStatus, true)); |
| | | List<DeviceDTO> devicesList = pagination.getRecords().stream().map(this::deviceEntityConvertToDTO) |
| | | .peek(device -> { |
| | | device.setStatus(RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn())); |
| | | device.setStatus(deviceRedisService.checkDeviceOnline(device.getDeviceSn())); |
| | | if (StringUtils.hasText(device.getChildDeviceSn())) { |
| | | Optional<DeviceDTO> childOpt = this.getDeviceBySn(device.getChildDeviceSn()); |
| | | childOpt.ifPresent(child -> { |
| | | child.setStatus( |
| | | RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + child.getDeviceSn())); |
| | | child.setStatus(deviceRedisService.checkDeviceOnline(child.getDeviceSn())); |
| | | child.setWorkspaceName(device.getWorkspaceName()); |
| | | device.setChildren(child); |
| | | }); |
| | |
| | | |
| | | @Override |
| | | public void unbindDevice(String deviceSn) { |
| | | String key = RedisConst.DEVICE_ONLINE_PREFIX + deviceSn; |
| | | DeviceDTO redisDevice = (DeviceDTO) RedisOpsUtils.get(key); |
| | | redisDevice.setWorkspaceId(""); |
| | | RedisOpsUtils.setWithExpire(key, redisDevice, RedisConst.DEVICE_ALIVE_SECOND); |
| | | |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(deviceSn); |
| | | if (deviceOpt.isPresent()) { |
| | | subDeviceOffline(deviceSn); |
| | | } else { |
| | | deviceOpt = getDeviceBySn(deviceSn); |
| | | } |
| | | if (deviceOpt.isEmpty()) { |
| | | return; |
| | | } |
| | | DeviceDTO device = DeviceDTO.builder() |
| | | .deviceSn(deviceSn) |
| | | .workspaceId("") |
| | |
| | | return Optional.empty(); |
| | | } |
| | | DeviceDTO device = devicesList.get(0); |
| | | device.setStatus(RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + sn)); |
| | | device.setStatus(deviceRedisService.checkDeviceOnline(sn)); |
| | | return Optional.of(device); |
| | | } |
| | | |
| | | @Override |
| | | /** |
| | | * Update the firmware version information of the device or payload. |
| | | * @param receiver |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_FIRMWARE_VERSION) |
| | | public void updateFirmwareVersion(FirmwareVersionReceiver receiver) { |
| | | // If the reported version is empty, it will not be processed to prevent misleading page. |
| | |
| | | return ResponseResult.error(); |
| | | } |
| | | |
| | | DeviceOtaCreateParam deviceOtaFirmware = deviceOtaFirmwares.get(0); |
| | | List<DeviceDTO> devices = getDevicesByParams(DeviceQueryParam.builder().childSn(deviceOtaFirmware.getSn()).build()); |
| | | String gatewaySn = devices.isEmpty() ? deviceOtaFirmware.getSn() : devices.get(0).getDeviceSn(); |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(deviceOtaFirmwares.get(0).getSn()); |
| | | if (deviceOpt.isEmpty()) { |
| | | throw new RuntimeException("Device is offline."); |
| | | } |
| | | DeviceDTO device = deviceOpt.get(); |
| | | String gatewaySn = DeviceDomainEnum.DOCK.getVal() == device.getDomain() ? device.getDeviceSn() : device.getParentSn(); |
| | | |
| | | String topic = THING_MODEL_PRE + PRODUCT + gatewaySn + SERVICES_SUF; |
| | | checkOtaConditions(gatewaySn); |
| | | |
| | | // The bids in the progress messages reported subsequently are the same. |
| | | String bid = UUID.randomUUID().toString(); |
| | | ServiceReply serviceReply = messageSender.publishWithReply( |
| | | topic, CommonTopicResponse.<Map<String, List<DeviceOtaCreateParam>>>builder() |
| | | .tid(UUID.randomUUID().toString()) |
| | | .bid(bid) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .method(FirmwareMethodEnum.OTA_CREATE.getMethod()) |
| | | .data(Map.of(MapKeyConst.DEVICES, deviceOtaFirmwares)) |
| | | .build()); |
| | | ServiceReply serviceReply = messageSender.publishServicesTopic( |
| | | gatewaySn, FirmwareMethodEnum.OTA_CREATE.getMethod(), Map.of(MapKeyConst.DEVICES, deviceOtaFirmwares), bid); |
| | | if (serviceReply.getResult() != ResponseResult.CODE_SUCCESS) { |
| | | return ResponseResult.error(serviceReply.getResult(), "Firmware Error Code: " + serviceReply.getResult()); |
| | | } |
| | | |
| | | // Record the device state that needs to be updated. |
| | | deviceOtaFirmwares.forEach(deviceOta -> RedisOpsUtils.setWithExpire( |
| | | RedisConst.FIRMWARE_UPGRADING_PREFIX + deviceOta.getSn(), |
| | | bid, |
| | | RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND)); |
| | | deviceOtaFirmwares.forEach(deviceOta -> deviceRedisService.setFirmwareUpgrading(deviceOta.getSn(), |
| | | EventsReceiver.<EventsOutputProgressReceiver<FirmwareProgressExtReceiver>>builder() |
| | | .bid(bid).sn(deviceOta.getSn()).build())); |
| | | return ResponseResult.success(); |
| | | } |
| | | |
| | | /** |
| | | * Determine whether the firmware can be upgraded. |
| | | * @param dockSn |
| | | */ |
| | | private void checkOtaConditions(String dockSn) { |
| | | Optional<OsdDockReceiver> deviceOpt = deviceRedisService.getDeviceOsd(dockSn, OsdDockReceiver.class); |
| | | if (deviceOpt.isEmpty()) { |
| | | throw new RuntimeException("Dock is offline."); |
| | | } |
| | | boolean emergencyStopState = deviceOpt.get().getEmergencyStopState(); |
| | | if (emergencyStopState) { |
| | | throw new RuntimeException("The emergency stop button of the dock is pressed and can't be upgraded."); |
| | | } |
| | | |
| | | DockModeCodeEnum dockMode = this.getDockMode(dockSn); |
| | | if (DockModeCodeEnum.IDLE != dockMode) { |
| | | throw new RuntimeException("The current status of the dock can't be upgraded."); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void devicePropertySet(String workspaceId, String dockSn, DeviceSetPropertyEnum propertyEnum, JsonNode param) { |
| | | boolean dockOnline = this.checkDeviceOnline(dockSn); |
| | | if (!dockOnline) { |
| | | Optional<DeviceDTO> dockOpt = deviceRedisService.getDeviceOnline(dockSn); |
| | | if (dockOpt.isEmpty()) { |
| | | throw new RuntimeException("Dock is offline."); |
| | | } |
| | | DeviceDTO deviceDTO = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + dockSn); |
| | | boolean deviceOnline = this.checkDeviceOnline(deviceDTO.getChildDeviceSn()); |
| | | if (!deviceOnline) { |
| | | String childSn = dockOpt.get().getChildDeviceSn(); |
| | | boolean deviceOnline = deviceRedisService.checkDeviceOnline(childSn); |
| | | Optional<OsdSubDeviceReceiver> osdOpt = deviceRedisService.getDeviceOsd(childSn, OsdSubDeviceReceiver.class); |
| | | if (!deviceOnline || osdOpt.isEmpty()) { |
| | | throw new RuntimeException("Device is offline."); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | String topic = THING_MODEL_PRE + PRODUCT + dockSn + PROPERTY_SUF + SET_SUF; |
| | | OsdSubDeviceReceiver osd = (OsdSubDeviceReceiver) RedisOpsUtils.get(RedisConst.OSD_PREFIX + deviceDTO.getChildDeviceSn()); |
| | | if (!param.isObject()) { |
| | | this.deviceOnePropertySet(topic, propertyEnum, Map.entry(propertyEnum.getProperty(), param)); |
| | | return; |
| | |
| | | // If there are multiple parameters, set them separately. |
| | | for (Iterator<Map.Entry<String, JsonNode>> filed = param.fields(); filed.hasNext(); ) { |
| | | Map.Entry<String, JsonNode> node = filed.next(); |
| | | boolean isPublish = basicDeviceProperty.canPublish(node.getKey(), osd); |
| | | boolean isPublish = basicDeviceProperty.canPublish(node.getKey(), osdOpt.get()); |
| | | if (!isPublish) { |
| | | continue; |
| | | } |
| | |
| | | .tid(UUID.randomUUID().toString()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data(value) |
| | | .build(), |
| | | 2); |
| | | .build()); |
| | | |
| | | while (true) { |
| | | reply = (Map<String, Object>) reply.get(value.getKey()); |
| | |
| | | |
| | | } |
| | | |
| | | public Boolean checkDeviceOnline(String sn) { |
| | | String key = RedisConst.DEVICE_ONLINE_PREFIX + sn; |
| | | return RedisOpsUtils.checkExist(key) && RedisOpsUtils.getExpire(key) > 0; |
| | | @Override |
| | | public DockModeCodeEnum getDockMode(String dockSn) { |
| | | return deviceRedisService.getDeviceOsd(dockSn, OsdDockReceiver.class) |
| | | .map(OsdDockReceiver::getModeCode).orElse(DockModeCodeEnum.DISCONNECTED); |
| | | } |
| | | |
| | | @Override |
| | | public DeviceModeCodeEnum getDeviceMode(String deviceSn) { |
| | | return deviceRedisService.getDeviceOsd(deviceSn, OsdSubDeviceReceiver.class) |
| | | .map(OsdSubDeviceReceiver::getModeCode).orElse(DeviceModeCodeEnum.DISCONNECTED); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean checkDockDrcMode(String dockSn) { |
| | | return deviceRedisService.getDeviceOsd(dockSn, OsdDockReceiver.class) |
| | | .map(OsdDockReceiver::getDrcState) |
| | | .orElse(DockDrcStateEnum.DISCONNECTED) != DockDrcStateEnum.DISCONNECTED; |
| | | } |
| | | |
| | | @Override |
| | | public Boolean checkAuthorityFlight(String gatewaySn) { |
| | | return deviceRedisService.getDeviceOnline(gatewaySn).flatMap(gateway -> |
| | | Optional.of((DeviceDomainEnum.DOCK.getVal() == gateway.getDomain() |
| | | || DeviceDomainEnum.GATEWAY.getVal() == gateway.getDomain()) |
| | | && ControlSourceEnum.A.getControlSource().equals(gateway.getControlSource()))) |
| | | .orElse(true); |
| | | } |
| | | |
| | | /** |
| | |
| | | .build(); |
| | | } |
| | | |
| | | private Optional<DeviceEntity> onlineSaveDevice(DeviceEntity device, String childSn) { |
| | | private Optional<DeviceEntity> onlineSaveDevice(DeviceEntity device, String childSn, String parentSn) { |
| | | |
| | | Optional<DeviceDTO> deviceOpt = this.getDeviceBySn(device.getDeviceSn()); |
| | | if (deviceOpt.isEmpty()) { |
| | |
| | | if (saveDeviceOpt.isEmpty()) { |
| | | return saveDeviceOpt; |
| | | } |
| | | device.setWorkspaceId(saveDeviceOpt.get().getWorkspaceId()); |
| | | |
| | | RedisOpsUtils.setWithExpire(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn(), |
| | | DeviceDTO.builder() |
| | | .deviceSn(device.getDeviceSn()) |
| | | .workspaceId(device.getWorkspaceId()) |
| | | .childDeviceSn(childSn) |
| | | .domain(device.getDomain()) |
| | | .type(device.getDeviceType()) |
| | | .subType(device.getSubType()) |
| | | .build(), |
| | | RedisConst.DEVICE_ALIVE_SECOND); |
| | | DeviceDTO redisDevice = this.deviceEntityConvertToDTO(saveDeviceOpt.get()); |
| | | redisDevice.setParentSn(parentSn); |
| | | |
| | | deviceRedisService.setDeviceOnline(redisDevice); |
| | | |
| | | return saveDeviceOpt; |
| | | } |
| | | |
| | | /** |
| | | * Handles messages in the state topic about basic drone data. |
| | | * |
| | | * Note: Only the data of the drone payload is handled here. You can handle other data from the drone |
| | | * according to your business needs. |
| | | * @param deviceBasic basic drone data |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_STATE_BASIC, outputChannel = ChannelName.INBOUND_STATE_PAYLOAD) |
| | | public List<DevicePayloadReceiver> stateBasic(DeviceBasicReceiver deviceBasic) { |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(deviceBasic.getDeviceSn()); |
| | | if (deviceOpt.isEmpty()) { |
| | | return deviceBasic.getPayloads(); |
| | | } |
| | | Optional<DeviceDTO> dockOpt = deviceRedisService.getDeviceOnline(deviceOpt.get().getParentSn()); |
| | | if (dockOpt.isEmpty()) { |
| | | return deviceBasic.getPayloads(); |
| | | } |
| | | DeviceDTO dock = dockOpt.get(); |
| | | if (!deviceBasic.getControlSource().equals(dock.getControlSource())) { |
| | | dock.setControlSource(deviceBasic.getControlSource()); |
| | | deviceRedisService.setDeviceOnline(dock); |
| | | |
| | | sendMessageService.sendBatch(dock.getWorkspaceId(), UserTypeEnum.WEB.getVal(), |
| | | BizCodeEnum.CONTROL_SOURCE_CHANGE.getCode(), |
| | | DeviceAuthorityDTO.builder() |
| | | .controlSource(dock.getControlSource()) |
| | | .sn(dock.getDeviceSn()) |
| | | .type(DroneAuthorityEnum.FLIGHT) |
| | | .build()); |
| | | |
| | | } |
| | | return deviceBasic.getPayloads(); |
| | | } |
| | | } |
| | |
| | | package com.dji.sample.manage.service.impl; |
| | | |
| | | import com.dji.sample.component.mqtt.model.CommonTopicReceiver; |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import com.dji.sample.component.redis.RedisOpsUtils; |
| | | import com.dji.sample.component.websocket.config.ConcurrentWebSocketSession; |
| | | import com.dji.sample.component.websocket.model.BizCodeEnum; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.Objects; |
| | | |
| | | /** |
| | | * @author sean |
| | |
| | | OsdDockReceiver data = mapper.convertValue(receiver.getData(), OsdDockReceiver.class); |
| | | wsMessage.getData().setHost(data); |
| | | sendMessageService.sendBatch(webSessions, wsMessage); |
| | | String key = RedisConst.OSD_PREFIX + device.getDeviceSn(); |
| | | OsdDockReceiver redisData = (OsdDockReceiver) RedisOpsUtils.get(key); |
| | | if (Objects.nonNull(data.getModeCode())) { |
| | | if (Objects.nonNull(redisData)) { |
| | | data.setDrcState(redisData.getDrcState()); |
| | | } |
| | | RedisOpsUtils.setWithExpire(key, data, RedisConst.DEVICE_ALIVE_SECOND); |
| | | return; |
| | | } |
| | | |
| | | if (Objects.nonNull(data.getDrcState()) && Objects.nonNull(redisData)) { |
| | | redisData.setDrcState(data.getDrcState()); |
| | | RedisOpsUtils.setWithExpire(key, redisData, RedisConst.DEVICE_ALIVE_SECOND); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | import com.dji.sample.manage.model.param.DeviceQueryParam; |
| | | import com.dji.sample.manage.model.receiver.CapacityDeviceReceiver; |
| | | import com.dji.sample.manage.model.receiver.LiveCapacityReceiver; |
| | | import com.dji.sample.manage.service.ICapacityCameraService; |
| | | import com.dji.sample.manage.service.IDeviceService; |
| | | import com.dji.sample.manage.service.ILiveStreamService; |
| | | import com.dji.sample.manage.service.IWorkspaceService; |
| | | import com.dji.sample.manage.service.*; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | |
| | | @Autowired |
| | | private IMessageSenderService messageSender; |
| | | |
| | | @Autowired |
| | | private IDeviceRedisService deviceRedisService; |
| | | |
| | | @Override |
| | | public List<CapacityDeviceDTO> getLiveCapacity(String workspaceId) { |
| | | |
| | |
| | | |
| | | // Query the live capability of each drone. |
| | | return devicesList.stream() |
| | | .filter(device -> RedisOpsUtils.checkExist(RedisConst.DEVICE_ONLINE_PREFIX + device.getDeviceSn())) |
| | | .filter(device -> deviceRedisService.checkDeviceOnline(device.getDeviceSn())) |
| | | .map(device -> CapacityDeviceDTO.builder() |
| | | .name(Objects.requireNonNullElse(device.getNickname(), device.getDeviceName())) |
| | | .sn(device.getDeviceSn()) |
| | |
| | | .toString()); |
| | | break; |
| | | case RTSP: |
| | | Object url = Objects.requireNonNullElse(receiveReply.getOutput(), receiveReply.getInfo()); |
| | | this.resolveUrlUser(String.valueOf(url), live); |
| | | String url = receiveReply.getOutput().toString(); |
| | | this.resolveUrlUser(url, live); |
| | | break; |
| | | case UNKNOWN: |
| | | return ResponseResult.error(LiveErrorEnum.URL_TYPE_NOT_SUPPORTED); |
| | |
| | | response.setMethod(LiveStreamMethodEnum.LIVE_LENS_CHANGE.getMethod()); |
| | | response.setData(liveParam); |
| | | |
| | | return messageSender.publishWithReply(respTopic, response); |
| | | return messageSender.publishWithReply(ServiceReply.class, respTopic, response); |
| | | } |
| | | |
| | | /** |
| | |
| | | response.setData(liveParam); |
| | | response.setMethod(LiveStreamMethodEnum.LIVE_START_PUSH.getMethod()); |
| | | |
| | | return messageSender.publishWithReply(topic, response); |
| | | return messageSender.publishWithReply(ServiceReply.class, topic, response); |
| | | } |
| | | |
| | | /** |
| | |
| | | response.setMethod(LiveStreamMethodEnum.LIVE_SET_QUALITY.getMethod()); |
| | | response.setData(data); |
| | | |
| | | return messageSender.publishWithReply(respTopic, response); |
| | | return messageSender.publishWithReply(ServiceReply.class, respTopic, response); |
| | | } |
| | | |
| | | /** |
| | |
| | | response.setData(data); |
| | | response.setMethod(LiveStreamMethodEnum.LIVE_STOP_PUSH.getMethod()); |
| | | |
| | | return messageSender.publishWithReply(topic, response); |
| | | return messageSender.publishWithReply(ServiceReply.class, topic, response); |
| | | } |
| | | |
| | | } |
| | |
| | | import com.dji.sample.common.model.CustomClaim; |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.websocket.model.BizCodeEnum; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | import com.dji.sample.component.websocket.service.ISendMessageService; |
| | | import com.dji.sample.component.websocket.service.IWebSocketManageService; |
| | | import com.dji.sample.map.model.dto.*; |
| | | import com.dji.sample.map.service.IWorkspaceElementService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | |
| | | |
| | | @Autowired |
| | | private ISendMessageService sendMessageService; |
| | | |
| | | @Autowired |
| | | private IWebSocketManageService webSocketManageService; |
| | | |
| | | /** |
| | | * In the first connection, pilot will send out this http request to obtain the group element list. |
| | |
| | | |
| | | // Notify all WebSocket connections in this workspace to be updated when an element is created. |
| | | elementService.getElementByElementId(elementCreate.getId()) |
| | | .ifPresent(groupElement -> |
| | | sendMessageService.sendBatch( |
| | | webSocketManageService.getValueWithWorkspace(workspaceId), |
| | | CustomWebSocketMessage.<GroupElementDTO>builder() |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(BizCodeEnum.MAP_ELEMENT_CREATE.getCode()) |
| | | .data(groupElement) |
| | | .build())); |
| | | .ifPresent(groupElement -> sendMessageService.sendBatch( |
| | | workspaceId, BizCodeEnum.MAP_ELEMENT_CREATE.getCode(), groupElement)); |
| | | |
| | | return ResponseResult.success(new ConcurrentHashMap<>(Map.of("id", elementCreate.getId()))); |
| | | } |
| | |
| | | |
| | | // Notify all WebSocket connections in this workspace to update when there is an element update. |
| | | elementService.getElementByElementId(elementId) |
| | | .ifPresent(groupElement -> |
| | | sendMessageService.sendBatch( |
| | | webSocketManageService.getValueWithWorkspace(workspaceId), |
| | | CustomWebSocketMessage.<GroupElementDTO>builder() |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(BizCodeEnum.MAP_ELEMENT_UPDATE.getCode()) |
| | | .data(groupElement) |
| | | .build())); |
| | | .ifPresent(groupElement -> sendMessageService.sendBatch( |
| | | workspaceId, BizCodeEnum.MAP_ELEMENT_UPDATE.getCode(), groupElement)); |
| | | return response; |
| | | } |
| | | |
| | |
| | | // Notify all WebSocket connections in this workspace to update when an element is deleted. |
| | | if (ResponseResult.CODE_SUCCESS == response.getCode()) { |
| | | elementOpt.ifPresent(element -> |
| | | sendMessageService.sendBatch( |
| | | webSocketManageService.getValueWithWorkspace(workspaceId), |
| | | CustomWebSocketMessage.<WebSocketElementDelDTO>builder() |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(BizCodeEnum.MAP_ELEMENT_DELETE.getCode()) |
| | | .data(WebSocketElementDelDTO.builder() |
| | | sendMessageService.sendBatch(workspaceId, BizCodeEnum.MAP_ELEMENT_DELETE.getCode(), |
| | | WebSocketElementDelDTO.builder() |
| | | .elementId(elementId) |
| | | .groupId(element.getGroupId()) |
| | | .build()) |
| | | .build())); |
| | | } |
| | | return response; |
| | |
| | | // Notify all WebSocket connections in this workspace to update when elements are deleted. |
| | | if (ResponseResult.CODE_SUCCESS == response.getCode()) { |
| | | |
| | | sendMessageService.sendBatch( |
| | | webSocketManageService.getValueWithWorkspace(workspaceId), |
| | | CustomWebSocketMessage.builder() |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(BizCodeEnum.MAP_GROUP_REFRESH.getCode()) |
| | | sendMessageService.sendBatch(workspaceId, BizCodeEnum.MAP_GROUP_REFRESH.getCode(), |
| | | // Group ids that need to re-request data |
| | | .data(new ConcurrentHashMap<String, String[]>(Map.of("ids", new String[]{groupId}))) |
| | | .build()); |
| | | new ConcurrentHashMap<String, String[]>(Map.of("ids", new String[]{groupId}))); |
| | | } |
| | | return response; |
| | | } |
| | |
| | | package com.dji.sample.media.service; |
| | | |
| | | import com.dji.sample.component.mqtt.model.CommonTopicReceiver; |
| | | import com.dji.sample.media.model.FileUploadDTO; |
| | | import org.springframework.messaging.MessageHeaders; |
| | | |
| | | import java.util.List; |
| | | |
| | |
| | | */ |
| | | List<String> getExistTinyFingerprints(String workspaceId, List<String> tinyFingerprints); |
| | | |
| | | /** |
| | | * Handle media files messages reported by dock. |
| | | * @param receiver |
| | | * @return |
| | | */ |
| | | void handleFileUploadCallBack(CommonTopicReceiver receiver); |
| | | |
| | | /** |
| | | * Handles the highest priority message about media uploads. |
| | | * @param receiver |
| | | * @param headers |
| | | */ |
| | | void handleHighestPriorityUploadFlightTaskMedia(CommonTopicReceiver receiver, MessageHeaders headers); |
| | | } |
| | |
| | | package com.dji.sample.media.service.impl; |
| | | |
| | | import com.dji.sample.common.error.CommonErrorEnum; |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.mqtt.model.*; |
| | | import com.dji.sample.component.mqtt.model.ChannelName; |
| | | import com.dji.sample.component.mqtt.model.CommonTopicReceiver; |
| | | import com.dji.sample.component.mqtt.model.MapKeyConst; |
| | | import com.dji.sample.component.mqtt.service.IMessageSenderService; |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import com.dji.sample.component.redis.RedisOpsUtils; |
| | | import com.dji.sample.component.websocket.model.BizCodeEnum; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | import com.dji.sample.component.websocket.service.ISendMessageService; |
| | | import com.dji.sample.component.websocket.service.IWebSocketManageService; |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.model.enums.UserTypeEnum; |
| | | import com.dji.sample.manage.service.IDeviceRedisService; |
| | | import com.dji.sample.manage.service.IDeviceService; |
| | | import com.dji.sample.media.model.FileUploadCallback; |
| | | import com.dji.sample.media.model.FileUploadDTO; |
| | |
| | | import com.dji.sample.wayline.model.dto.WaylineJobDTO; |
| | | import com.dji.sample.wayline.service.IWaylineJobService; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.integration.annotation.ServiceActivator; |
| | | import org.springframework.integration.mqtt.support.MqttHeaders; |
| | | import org.springframework.messaging.MessageHeaders; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.Optional; |
| | | import java.util.stream.Collectors; |
| | | |
| | |
| | | * @date 2021/12/9 |
| | | */ |
| | | @Service |
| | | @Slf4j |
| | | public class MediaServiceImpl implements IMediaService { |
| | | |
| | | @Autowired |
| | |
| | | private ISendMessageService sendMessageService; |
| | | |
| | | @Autowired |
| | | private IWebSocketManageService webSocketManageService; |
| | | private IDeviceRedisService deviceRedisService; |
| | | |
| | | @Override |
| | | public Boolean fastUpload(String workspaceId, String fingerprint) { |
| | |
| | | |
| | | } |
| | | |
| | | @Override |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FILE_UPLOAD_CALLBACK, outputChannel = ChannelName.OUTBOUND) |
| | | public void handleFileUploadCallBack(CommonTopicReceiver receiver) { |
| | | /** |
| | | * Handle media files messages reported by dock. |
| | | * @param receiver |
| | | * @return |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FILE_UPLOAD_CALLBACK, outputChannel = ChannelName.OUTBOUND_EVENTS) |
| | | public CommonTopicReceiver handleFileUploadCallBack(CommonTopicReceiver receiver) { |
| | | FileUploadCallback callback = objectMapper.convertValue(receiver.getData(), FileUploadCallback.class); |
| | | |
| | | String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + receiver.getGateway() |
| | | + TopicConst.EVENTS_SUF + TopicConst._REPLY_SUF; |
| | | CommonTopicResponse<RequestsReply> data = CommonTopicResponse.<RequestsReply>builder() |
| | | .timestamp(System.currentTimeMillis()) |
| | | .method(EventsMethodEnum.FILE_UPLOAD_CALLBACK.getMethod()) |
| | | .data(RequestsReply.success()) |
| | | .tid(receiver.getTid()) |
| | | .bid(receiver.getBid()) |
| | | .build(); |
| | | |
| | | if (callback.getResult() != ResponseResult.CODE_SUCCESS) { |
| | | messageSenderService.publish(topic, data); |
| | | return; |
| | | log.error("Media file upload failed!"); |
| | | return null; |
| | | } |
| | | |
| | | String jobId = callback.getFile().getExt().getFlightId(); |
| | | |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(receiver.getGateway()); |
| | | MediaFileCountDTO mediaFileCount = (MediaFileCountDTO) RedisOpsUtils.hashGet(RedisConst.MEDIA_FILE_PREFIX + receiver.getGateway(), jobId); |
| | | // duplicate data |
| | | if (receiver.getBid().equals(mediaFileCount.getBid()) && receiver.getTid().equals(mediaFileCount.getTid())) { |
| | | messageSenderService.publish(topic, data); |
| | | return; |
| | | if (deviceOpt.isEmpty() |
| | | || (Objects.nonNull(mediaFileCount) && receiver.getBid().equals(mediaFileCount.getBid()) |
| | | && receiver.getTid().equals(mediaFileCount.getTid()))) { |
| | | return receiver; |
| | | } |
| | | |
| | | DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + receiver.getGateway()); |
| | | |
| | | DeviceDTO device = deviceOpt.get(); |
| | | Optional<WaylineJobDTO> jobOpt = waylineJobService.getJobByJobId(device.getWorkspaceId(), jobId); |
| | | if (jobOpt.isPresent()) { |
| | | boolean isSave = parseMediaFile(callback, jobOpt.get()); |
| | | if (!isSave) { |
| | | data.setData(RequestsReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT)); |
| | | log.error("Failed to save the file to the database, please check the data manually."); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | messageSenderService.publish(topic, data); |
| | | |
| | | notifyUploadedCount(mediaFileCount, receiver, jobId); |
| | | notifyUploadedCount(mediaFileCount, receiver, jobId, device); |
| | | return receiver; |
| | | } |
| | | |
| | | /** |
| | |
| | | * @param receiver |
| | | * @param jobId |
| | | */ |
| | | private void notifyUploadedCount(MediaFileCountDTO mediaFileCount, CommonTopicReceiver receiver, String jobId) { |
| | | private void notifyUploadedCount(MediaFileCountDTO mediaFileCount, CommonTopicReceiver receiver, String jobId, DeviceDTO dock) { |
| | | // Do not notify when files that do not belong to the route are uploaded. |
| | | if (Objects.isNull(mediaFileCount)) { |
| | | return; |
| | | } |
| | | mediaFileCount.setBid(receiver.getBid()); |
| | | mediaFileCount.setTid(receiver.getTid()); |
| | | mediaFileCount.setUploadedCount(mediaFileCount.getUploadedCount() + 1); |
| | |
| | | RedisOpsUtils.hashSet(key, jobId, mediaFileCount); |
| | | } |
| | | |
| | | DeviceDTO dock = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + receiver.getGateway()); |
| | | sendMessageService.sendBatch(webSocketManageService.getValueWithWorkspaceAndUserType(dock.getWorkspaceId(), UserTypeEnum.WEB.getVal()), |
| | | CustomWebSocketMessage.builder() |
| | | .bizCode(BizCodeEnum.FILE_UPLOAD_CALLBACK.getCode()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data(mediaFileCount) |
| | | .build()); |
| | | sendMessageService.sendBatch(dock.getWorkspaceId(), UserTypeEnum.WEB.getVal(), |
| | | BizCodeEnum.FILE_UPLOAD_CALLBACK.getCode(), mediaFileCount); |
| | | } |
| | | |
| | | private Boolean parseMediaFile(FileUploadCallback callback, WaylineJobDTO job) { |
| | |
| | | return fileService.saveFile(job.getWorkspaceId(), callback.getFile()) > 0; |
| | | } |
| | | |
| | | @Override |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA, outputChannel = ChannelName.OUTBOUND) |
| | | public void handleHighestPriorityUploadFlightTaskMedia(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | /** |
| | | * Handles the highest priority message about media uploads. |
| | | * @param receiver |
| | | * @param headers |
| | | * @return |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA, outputChannel = ChannelName.OUTBOUND_EVENTS) |
| | | public CommonTopicReceiver handleHighestPriorityUploadFlightTaskMedia(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | Map map = objectMapper.convertValue(receiver.getData(), Map.class); |
| | | if (map.isEmpty() || !map.containsKey(MapKeyConst.FLIGHT_ID)) { |
| | | return; |
| | | return null; |
| | | } |
| | | |
| | | messageSenderService.publish(headers.get(MqttHeaders.RECEIVED_TOPIC) + TopicConst._REPLY_SUF, |
| | | CommonTopicResponse.builder() |
| | | .data(RequestsReply.success()) |
| | | .method(receiver.getMethod()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bid(receiver.getBid()) |
| | | .tid(receiver.getTid()) |
| | | .build()); |
| | | |
| | | String dockSn = receiver.getGateway(); |
| | | String jobId = map.get(MapKeyConst.FLIGHT_ID).toString(); |
| | |
| | | countDTO = (MediaFileCountDTO) RedisOpsUtils.get(key); |
| | | if (jobId.equals(countDTO.getJobId())) { |
| | | RedisOpsUtils.setWithExpire(key, countDTO, RedisConst.DEVICE_ALIVE_SECOND * 5); |
| | | return; |
| | | return null; |
| | | } |
| | | |
| | | countDTO.setPreJobId(countDTO.getJobId()); |
| | |
| | | |
| | | RedisOpsUtils.setWithExpire(key, countDTO, RedisConst.DEVICE_ALIVE_SECOND * 5); |
| | | |
| | | DeviceDTO dock = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + dockSn); |
| | | sendMessageService.sendBatch(webSocketManageService.getValueWithWorkspaceAndUserType(dock.getWorkspaceId(), UserTypeEnum.WEB.getVal()), |
| | | CustomWebSocketMessage.builder() |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(BizCodeEnum.HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA.getCode()) |
| | | .data(countDTO) |
| | | .build()); |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(receiver.getGateway()); |
| | | if (deviceOpt.isEmpty()) { |
| | | return null; |
| | | } |
| | | sendMessageService.sendBatch(deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), |
| | | BizCodeEnum.HIGHEST_PRIORITY_UPLOAD_FLIGHT_TASK_MEDIA.getCode(), countDTO); |
| | | |
| | | return receiver; |
| | | } |
| | | } |
| | |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.wayline.model.dto.WaylineJobDTO; |
| | | import com.dji.sample.wayline.model.param.CreateJobParam; |
| | | import com.dji.sample.wayline.model.param.UpdateJobParam; |
| | | import com.dji.sample.wayline.service.IWaylineJobService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.*; |
| | |
| | | waylineJobService.uploadMediaHighestPriority(workspaceId, jobId); |
| | | return ResponseResult.success(); |
| | | } |
| | | |
| | | @PutMapping("/{workspace_id}/jobs/{job_id}") |
| | | public ResponseResult updateJobStatus(@PathVariable(name = "workspace_id") String workspaceId, |
| | | @PathVariable(name = "job_id") String jobId, |
| | | @Valid @RequestBody UpdateJobParam param) { |
| | | waylineJobService.updateJobStatus(workspaceId, jobId, param); |
| | | return ResponseResult.success(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.dji.sample.wayline.model.dto; |
| | | |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Objects; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/28 |
| | | */ |
| | | @Data |
| | | public class ConditionalWaylineJobKey { |
| | | |
| | | private String workspaceId; |
| | | |
| | | private String dockSn; |
| | | |
| | | private String jobId; |
| | | |
| | | public ConditionalWaylineJobKey(String workspaceId, String dockSn, String jobId) { |
| | | this.workspaceId = workspaceId; |
| | | this.dockSn = dockSn; |
| | | this.jobId = jobId; |
| | | } |
| | | |
| | | public ConditionalWaylineJobKey(String key) { |
| | | if (Objects.isNull(key)) { |
| | | return; |
| | | } |
| | | String[] keyArr = key.split(RedisConst.DELIMITER); |
| | | new ConditionalWaylineJobKey(keyArr[0], keyArr[1], keyArr[2]); |
| | | } |
| | | |
| | | public String getKey() { |
| | | return String.join(RedisConst.DELIMITER, workspaceId, dockSn, jobId); |
| | | } |
| | | } |
| | |
| | | |
| | | public static final String TAG_PAYLOAD_SUB_ENUM_VALUE = "payloadSubEnumValue"; |
| | | |
| | | public static final String TAG_TEMPLATE_ID = "templateId"; |
| | | public static final String TAG_TEMPLATE_TYPE = "templateType"; |
| | | } |
| | |
| | | |
| | | private Boolean uploading; |
| | | |
| | | private WaylineTaskConditionDTO conditions; |
| | | |
| | | private String parentId; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.wayline.model.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/2/16 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class WaylineTaskConditionDTO { |
| | | |
| | | private WaylineTaskReadyConditionDTO readyConditions; |
| | | |
| | | private WaylineTaskExecutableConditionDTO executableConditions; |
| | | } |
| | |
| | | |
| | | private Integer outOfControlAction; |
| | | |
| | | private WaylineTaskReadyConditionDTO readyConditions; |
| | | |
| | | private WaylineTaskExecutableConditionDTO executableConditions; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.wayline.model.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/2/16 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class WaylineTaskExecutableConditionDTO { |
| | | |
| | | /** |
| | | * unit: MB |
| | | */ |
| | | private Integer storageCapacity; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.wayline.model.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/2/16 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class WaylineTaskReadyConditionDTO { |
| | | |
| | | private Integer batteryCapacity; |
| | | |
| | | private Long beginTime; |
| | | |
| | | private Long endTime; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.wayline.model.enums; |
| | | |
| | | import com.dji.sample.common.error.IErrorInfo; |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/2/17 |
| | | */ |
| | | public enum WaylineErrorCodeEnum implements IErrorInfo { |
| | | |
| | | SUCCESS(0, "success", false), |
| | | |
| | | EMERGENCY_BUTTON(316026, "The emergency button at the dock was pressed.", true), |
| | | |
| | | NOT_IDLE(319001, "Task Center is not currently idle.", true), |
| | | |
| | | PERFORMING_TASK(319016, "The dock is performing other tasks.", true), |
| | | |
| | | EXPORTING_LOGS(319018, "The dock is exporting logs.", true), |
| | | |
| | | PULLING_LOGS(319019, "The dock is pulling logs.", true), |
| | | |
| | | HEIGHT_LIMIT(321513, "The wayline altitude has exceeded the height limit of the drone.", true), |
| | | |
| | | DISTANCE_LIMIT(321514, "The wayline distance has exceeded the limit of the drone.", true), |
| | | |
| | | RESTRICTED_FLIGHT_AREA(321515, "The wayline passes through a restricted flight area.", true), |
| | | |
| | | SDR_DISCONNECT(514120, "The sdr link between the dock and the drone is disconnected.", true), |
| | | |
| | | HEAVY_RAIN(514134, "Heavy rain prevented the flight.", true), |
| | | |
| | | STRONG_WIND(514135, "Strong wind prevented the flight.", true), |
| | | |
| | | POWER_DISCONNECT(514136, "The dock's power supply is disconnected.", true), |
| | | |
| | | LOW_TEMPERATURE(514137, "The low temperature of the environment prevented flight.", true), |
| | | |
| | | DEBUGGING(514145, "The dock is being debugged.", true), |
| | | |
| | | REMOTE_DEBUGGING(514146, "The dock is being debugged remotely.", true), |
| | | |
| | | DOCK_UPGRADING(514147, "The dock is being upgraded.", true), |
| | | |
| | | DOCK_WORKING(514148, "The dock is working and cannot perform new tasks.", true), |
| | | |
| | | UNKNOWN(-1, "Unknown wayline error.", false); |
| | | |
| | | private String msg; |
| | | |
| | | private int code; |
| | | |
| | | boolean block; |
| | | |
| | | WaylineErrorCodeEnum(int code, String msg, boolean block) { |
| | | this.code = code; |
| | | this.msg = msg; |
| | | this.block = block; |
| | | } |
| | | |
| | | @Override |
| | | public String getErrorMsg() { |
| | | return msg; |
| | | } |
| | | |
| | | @Override |
| | | public Integer getErrorCode() { |
| | | return code; |
| | | } |
| | | |
| | | public boolean isBlock() { |
| | | return block; |
| | | } |
| | | |
| | | @JsonCreator(mode = JsonCreator.Mode.DELEGATING) |
| | | public static WaylineErrorCodeEnum find(int code) { |
| | | return Arrays.stream(WaylineErrorCodeEnum.values()).filter(error -> error.code == code).findAny().orElse(UNKNOWN); |
| | | } |
| | | } |
| | |
| | | |
| | | FLIGHT_TASK_EXECUTE("flighttask_execute"), |
| | | |
| | | FLIGHT_TASK_CANCEL("flighttask_undo"); |
| | | FLIGHT_TASK_CANCEL("flighttask_undo"), |
| | | |
| | | FLIGHT_TASK_PAUSE("flighttask_pause"), |
| | | |
| | | FLIGHT_TASK_RESUME("flighttask_recovery"); |
| | | |
| | | private String method; |
| | | |
| New file |
| | |
| | | package com.dji.sample.wayline.model.enums; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonValue; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/2/1 |
| | | */ |
| | | public enum WaylineTaskStatusEnum { |
| | | |
| | | PAUSE, RESUME; |
| | | |
| | | @JsonValue |
| | | public int getVal() { |
| | | return ordinal(); |
| | | } |
| | | |
| | | } |
| | |
| | | package com.dji.sample.wayline.model.enums; |
| | | |
| | | import lombok.Getter; |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | import com.fasterxml.jackson.annotation.JsonValue; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/9/26 |
| | | */ |
| | | @Getter |
| | | public enum WaylineTaskTypeEnum { |
| | | |
| | | IMMEDIATE(0), |
| | | |
| | | TIMED(1); |
| | | TIMED(1), |
| | | |
| | | CONDITION(2); |
| | | |
| | | int val; |
| | | |
| | | WaylineTaskTypeEnum(int val) { |
| | | this.val = val; |
| | | } |
| | | |
| | | @JsonValue |
| | | public int getVal() { |
| | | return val; |
| | | } |
| | | |
| | | @JsonCreator |
| | | public static WaylineTaskTypeEnum find(Integer val) { |
| | | return Arrays.stream(values()).filter(taskTypeEnum -> taskTypeEnum.val == val).findAny().get(); |
| | | } |
| | | } |
| | |
| | | package com.dji.sample.wayline.model.enums; |
| | | |
| | | import lombok.Getter; |
| | | import com.fasterxml.jackson.annotation.JsonCreator; |
| | | import com.fasterxml.jackson.annotation.JsonValue; |
| | | |
| | | import java.util.Arrays; |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2022/9/26 |
| | | */ |
| | | @Getter |
| | | public enum WaylineTemplateTypeEnum { |
| | | |
| | | WAYPOINT(0), |
| | | WAYPOINT(0, "waypoint"), |
| | | |
| | | MAPPING_2D(1), |
| | | MAPPING_2D(1, "mapping2d"), |
| | | |
| | | MAPPING_3D(2), |
| | | MAPPING_3D(2, "mapping3d"), |
| | | |
| | | MAPPING_STRIP(4); |
| | | MAPPING_STRIP(4, "mappingStrip"); |
| | | |
| | | int val; |
| | | |
| | | WaylineTemplateTypeEnum(int val) { |
| | | String type; |
| | | |
| | | WaylineTemplateTypeEnum(int val, String type) { |
| | | this.val = val; |
| | | this.type = type; |
| | | } |
| | | |
| | | @JsonValue |
| | | public int getVal() { |
| | | return val; |
| | | } |
| | | |
| | | @JsonCreator(mode = JsonCreator.Mode.DELEGATING) |
| | | public static WaylineTemplateTypeEnum find(Integer val) { |
| | | return Arrays.stream(values()).filter(templateTypeEnum -> templateTypeEnum.val == val).findAny().get(); |
| | | } |
| | | |
| | | public static Optional<WaylineTemplateTypeEnum> find(String type) { |
| | | return Arrays.stream(values()).filter(templateTypeEnum -> templateTypeEnum.type.equals(type)).findAny(); |
| | | } |
| | | } |
| | |
| | | package com.dji.sample.wayline.model.param; |
| | | |
| | | import com.dji.sample.wayline.model.enums.WaylineTaskTypeEnum; |
| | | import com.dji.sample.wayline.model.enums.WaylineTemplateTypeEnum; |
| | | import lombok.Data; |
| | | import org.hibernate.validator.constraints.Range; |
| | | |
| | | import javax.validation.constraints.NotBlank; |
| | | import javax.validation.constraints.NotNull; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * @author sean |
| | |
| | | @NotBlank |
| | | private String dockSn; |
| | | |
| | | @Range(max = 0) |
| | | @NotNull |
| | | private Integer waylineType; |
| | | private WaylineTemplateTypeEnum waylineType; |
| | | |
| | | @Range(max = 1) |
| | | @NotNull |
| | | private Integer taskType; |
| | | |
| | | private Long executeTime; |
| | | private WaylineTaskTypeEnum taskType; |
| | | |
| | | @Range(min = 20, max = 500) |
| | | @NotNull |
| | |
| | | @NotNull |
| | | @Range(max = 2) |
| | | private Integer outOfControlAction; |
| | | |
| | | @Range(min = 50, max = 90) |
| | | private Integer minBatteryCapacity; |
| | | |
| | | private Integer minStorageCapacity; |
| | | |
| | | private List<Long> taskDays; |
| | | |
| | | private List<List<Long>> taskPeriods; |
| | | } |
| New file |
| | |
| | | package com.dji.sample.wayline.model.param; |
| | | |
| | | import com.dji.sample.wayline.model.enums.WaylineTaskStatusEnum; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.3 |
| | | * @date 2023/2/1 |
| | | */ |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class UpdateJobParam { |
| | | |
| | | private WaylineTaskStatusEnum status; |
| | | |
| | | } |
| | |
| | | package com.dji.sample.wayline.service; |
| | | |
| | | import com.dji.sample.component.mqtt.model.CommonTopicReceiver; |
| | | import org.springframework.messaging.MessageHeaders; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.1 |
| | |
| | | */ |
| | | public interface IFlightTaskService { |
| | | |
| | | /** |
| | | * Handle the progress messages of the flight tasks reported by the dock. |
| | | * @param receiver |
| | | */ |
| | | void handleProgress(CommonTopicReceiver receiver, MessageHeaders headers); |
| | | |
| | | } |
| | |
| | | import com.dji.sample.wayline.model.dto.WaylineJobDTO; |
| | | import com.dji.sample.wayline.model.enums.WaylineJobStatusEnum; |
| | | import com.dji.sample.wayline.model.param.CreateJobParam; |
| | | import com.dji.sample.wayline.model.param.UpdateJobParam; |
| | | import org.springframework.messaging.MessageHeaders; |
| | | |
| | | import java.sql.SQLException; |
| | |
| | | * @return |
| | | */ |
| | | ResponseResult publishFlightTask(CreateJobParam param, CustomClaim customClaim) throws SQLException; |
| | | |
| | | /** |
| | | * Issue wayline mission to the dock. |
| | | * @param waylineJob |
| | | * @return |
| | | * @throws SQLException |
| | | */ |
| | | ResponseResult publishOneFlightTask(WaylineJobDTO waylineJob) throws SQLException; |
| | | |
| | | /** |
| | | * Execute the task immediately. |
| | |
| | | * @param jobId |
| | | */ |
| | | void uploadMediaHighestPriority(String workspaceId, String jobId); |
| | | |
| | | /** |
| | | * Manually control the execution status of wayline job. |
| | | * @param workspaceId |
| | | * @param jobId |
| | | * @param param |
| | | */ |
| | | void updateJobStatus(String workspaceId, String jobId, UpdateJobParam param); |
| | | |
| | | /** |
| | | * Query the wayline execution status of the dock. |
| | | * @param dockSn |
| | | * @return |
| | | */ |
| | | WaylineJobStatusEnum getWaylineState(String dockSn); |
| | | } |
| New file |
| | |
| | | package com.dji.sample.wayline.service; |
| | | |
| | | import com.dji.sample.component.mqtt.model.EventsReceiver; |
| | | import com.dji.sample.wayline.model.dto.ConditionalWaylineJobKey; |
| | | import com.dji.sample.wayline.model.dto.WaylineJobDTO; |
| | | import com.dji.sample.wayline.model.dto.WaylineTaskProgressReceiver; |
| | | |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/24 |
| | | */ |
| | | public interface IWaylineRedisService { |
| | | |
| | | /** |
| | | * Save the status of the wayline job performed by the dock into redis. |
| | | * @param dockSn |
| | | * @param data |
| | | */ |
| | | void setRunningWaylineJob(String dockSn, EventsReceiver<WaylineTaskProgressReceiver> data); |
| | | |
| | | /** |
| | | * Query the status of wayline job performed by the dock in redis. |
| | | * @param dockSn |
| | | * @return |
| | | */ |
| | | Optional<EventsReceiver<WaylineTaskProgressReceiver>> getRunningWaylineJob(String dockSn); |
| | | |
| | | /** |
| | | * Delete the wayline job status of the dock operation in redis. |
| | | * @param dockSn |
| | | * @return |
| | | */ |
| | | Boolean delRunningWaylineJob(String dockSn); |
| | | |
| | | /** |
| | | * Save the wayline job suspended by the dock to redis. |
| | | * @param dockSn |
| | | * @param jobId |
| | | */ |
| | | void setPausedWaylineJob(String dockSn, String jobId); |
| | | |
| | | /** |
| | | * Query the wayline job id suspended by the dock in redis. |
| | | * @param dockSn |
| | | * @return |
| | | */ |
| | | String getPausedWaylineJobId(String dockSn); |
| | | |
| | | /** |
| | | * Delete the wayline job suspended by the dock in redis. |
| | | * @param dockSn |
| | | * @return |
| | | */ |
| | | Boolean delPausedWaylineJob(String dockSn); |
| | | |
| | | /** |
| | | * Save the wayline job blocked by the dock to redis. |
| | | * @param dockSn |
| | | * @param jobId |
| | | */ |
| | | void setBlockedWaylineJob(String dockSn, String jobId); |
| | | |
| | | /** |
| | | * Query the wayline job id blocked by the dock in redis. |
| | | * @param dockSn |
| | | * @return |
| | | */ |
| | | String getBlockedWaylineJobId(String dockSn); |
| | | |
| | | /** |
| | | * Save the conditional wayline job by the dock to redis. |
| | | * @param waylineJob |
| | | */ |
| | | void setConditionalWaylineJob(WaylineJobDTO waylineJob); |
| | | |
| | | /** |
| | | * Query the conditional wayline job id by the dock in redis. |
| | | * @param jobId |
| | | * @return |
| | | */ |
| | | Optional<WaylineJobDTO> getConditionalWaylineJob(String jobId); |
| | | |
| | | /** |
| | | * Delete the conditional wayline job by the dock in redis. |
| | | * @param jobId |
| | | * @return |
| | | */ |
| | | Boolean delConditionalWaylineJob(String jobId); |
| | | |
| | | Boolean addPrepareConditionalWaylineJob(WaylineJobDTO waylineJob); |
| | | |
| | | Optional<ConditionalWaylineJobKey> getNearestConditionalWaylineJob(); |
| | | |
| | | Double getConditionalWaylineJobTime(ConditionalWaylineJobKey jobKey); |
| | | |
| | | Boolean removePrepareConditionalWaylineJob(ConditionalWaylineJobKey jobKey); |
| | | } |
| | |
| | | package com.dji.sample.wayline.service.impl; |
| | | |
| | | import com.dji.sample.common.error.CommonErrorEnum; |
| | | import com.dji.sample.common.model.ResponseResult; |
| | | import com.dji.sample.component.mqtt.model.*; |
| | | import com.dji.sample.component.mqtt.service.IMessageSenderService; |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import com.dji.sample.component.redis.RedisOpsUtils; |
| | | import com.dji.sample.component.websocket.model.BizCodeEnum; |
| | | import com.dji.sample.component.websocket.model.CustomWebSocketMessage; |
| | | import com.dji.sample.component.websocket.service.ISendMessageService; |
| | | import com.dji.sample.component.websocket.service.IWebSocketManageService; |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.model.enums.UserTypeEnum; |
| | | import com.dji.sample.manage.service.IDeviceRedisService; |
| | | import com.dji.sample.media.model.MediaFileCountDTO; |
| | | import com.dji.sample.wayline.model.dto.ConditionalWaylineJobKey; |
| | | import com.dji.sample.wayline.model.dto.WaylineJobDTO; |
| | | import com.dji.sample.wayline.model.dto.WaylineTaskProgressReceiver; |
| | | import com.dji.sample.wayline.model.enums.WaylineJobStatusEnum; |
| | | import com.dji.sample.wayline.service.IFlightTaskService; |
| | | import com.dji.sample.wayline.service.IWaylineJobService; |
| | | import com.dji.sample.wayline.service.IWaylineRedisService; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | import org.springframework.messaging.MessageHeaders; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.time.ZoneId; |
| | | import java.util.*; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | |
| | | private ISendMessageService websocketMessageService; |
| | | |
| | | @Autowired |
| | | private IWebSocketManageService webSocketManageService; |
| | | |
| | | @Autowired |
| | | private IWaylineJobService waylineJobService; |
| | | |
| | | @Override |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FLIGHT_TASK_PROGRESS, outputChannel = ChannelName.OUTBOUND) |
| | | public void handleProgress(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | @Autowired |
| | | private IDeviceRedisService deviceRedisService; |
| | | |
| | | @Autowired |
| | | private IWaylineRedisService waylineRedisService; |
| | | |
| | | /** |
| | | * Handle the progress messages of the flight tasks reported by the dock. |
| | | * @param receiver |
| | | * @param headers |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FLIGHT_TASK_PROGRESS, outputChannel = ChannelName.OUTBOUND_EVENTS) |
| | | public CommonTopicReceiver handleProgress(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | String receivedTopic = String.valueOf(headers.get(MqttHeaders.RECEIVED_TOPIC)); |
| | | String dockSn = receivedTopic.substring((TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT).length(), |
| | | receivedTopic.indexOf(TopicConst.EVENTS_SUF)); |
| | |
| | | } |
| | | |
| | | EventsResultStatusEnum statusEnum = EventsResultStatusEnum.find(output.getStatus()); |
| | | String key = RedisConst.WAYLINE_JOB_RUNNING_PREFIX + dockSn; |
| | | RedisOpsUtils.setWithExpire(key, eventsReceiver, RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND); |
| | | waylineRedisService.setRunningWaylineJob(dockSn, eventsReceiver); |
| | | |
| | | if (statusEnum.getEnd()) { |
| | | WaylineJobDTO job = WaylineJobDTO.builder() |
| | |
| | | } |
| | | |
| | | waylineJobService.updateJob(job); |
| | | RedisOpsUtils.del(key); |
| | | RedisOpsUtils.del(RedisConst.WAYLINE_JOB_PAUSED_PREFIX + receiver.getBid()); |
| | | waylineRedisService.delRunningWaylineJob(dockSn); |
| | | waylineRedisService.delPausedWaylineJob(receiver.getBid()); |
| | | } |
| | | |
| | | DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + receiver.getGateway()); |
| | | websocketMessageService.sendBatch( |
| | | webSocketManageService.getValueWithWorkspaceAndUserType( |
| | | device.getWorkspaceId(), UserTypeEnum.WEB.getVal()), |
| | | CustomWebSocketMessage.builder() |
| | | .data(eventsReceiver) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .bizCode(BizCodeEnum.FLIGHT_TASK_PROGRESS.getCode()) |
| | | .build()); |
| | | |
| | | if (receiver.getNeedReply() == 1) { |
| | | messageSender.publish(receivedTopic + TopicConst._REPLY_SUF, |
| | | CommonTopicResponse.builder() |
| | | .tid(receiver.getTid()) |
| | | .bid(receiver.getBid()) |
| | | .method(EventsMethodEnum.FLIGHT_TASK_PROGRESS.getMethod()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data(RequestsReply.success()) |
| | | .build()); |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(receiver.getGateway()); |
| | | if (deviceOpt.isEmpty()) { |
| | | return null; |
| | | } |
| | | websocketMessageService.sendBatch(deviceOpt.get().getWorkspaceId(), UserTypeEnum.WEB.getVal(), |
| | | BizCodeEnum.FLIGHT_TASK_PROGRESS.getCode(), eventsReceiver); |
| | | |
| | | return receiver; |
| | | } |
| | | |
| | | /** |
| | | * Notifications will be received through this interface when tasks are ready on the device. |
| | | * @param receiver |
| | | * @param headers |
| | | */ |
| | | @ServiceActivator(inputChannel = ChannelName.INBOUND_EVENTS_FLIGHT_TASK_READY, outputChannel = ChannelName.OUTBOUND_EVENTS) |
| | | public CommonTopicReceiver handleTaskNotifications(CommonTopicReceiver receiver, MessageHeaders headers) { |
| | | String receivedTopic = String.valueOf(headers.get(MqttHeaders.RECEIVED_TOPIC)); |
| | | String dockSn = receivedTopic.substring((TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT).length(), |
| | | receivedTopic.indexOf(TopicConst.EVENTS_SUF)); |
| | | List<String> flightIds = mapper.convertValue(receiver.getData(), |
| | | new TypeReference<Map<String, List<String>>>(){}).get(MapKeyConst.FLIGHT_IDS); |
| | | |
| | | log.info("ready task list:{}", Arrays.toString(flightIds.toArray()) ); |
| | | // Check conditional task blocking status. |
| | | String blockedId = waylineRedisService.getBlockedWaylineJobId(dockSn); |
| | | if (!StringUtils.hasText(blockedId)) { |
| | | return null; |
| | | } |
| | | |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(dockSn); |
| | | if (deviceOpt.isEmpty()) { |
| | | return null; |
| | | } |
| | | DeviceDTO device = deviceOpt.get(); |
| | | |
| | | try { |
| | | for (String jobId : flightIds) { |
| | | boolean isExecute = waylineJobService.executeFlightTask(device.getWorkspaceId(), jobId); |
| | | if (!isExecute) { |
| | | return null; |
| | | } |
| | | Optional<WaylineJobDTO> waylineJobOpt = waylineRedisService.getConditionalWaylineJob(jobId); |
| | | if (waylineJobOpt.isEmpty()) { |
| | | log.info("The conditional job has expired and will no longer be executed."); |
| | | return receiver; |
| | | } |
| | | WaylineJobDTO waylineJob = waylineJobOpt.get(); |
| | | this.retryPrepareJob(new ConditionalWaylineJobKey(device.getWorkspaceId(), dockSn, jobId), waylineJob); |
| | | return receiver; |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("Failed to execute conditional task."); |
| | | e.printStackTrace(); |
| | | } |
| | | return receiver; |
| | | } |
| | | |
| | | @Scheduled(initialDelay = 10, fixedRate = 5, timeUnit = TimeUnit.SECONDS) |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Scheduled(initialDelay = 10, fixedRate = 5, timeUnit = TimeUnit.SECONDS) |
| | | private void prepareConditionJob() { |
| | | Optional<ConditionalWaylineJobKey> jobKeyOpt = waylineRedisService.getNearestConditionalWaylineJob(); |
| | | if (jobKeyOpt.isEmpty()) { |
| | | return; |
| | | } |
| | | ConditionalWaylineJobKey jobKey = jobKeyOpt.get(); |
| | | log.info("Check the conditional tasks of the wayline. {}", jobKey.toString()); |
| | | // format: {workspace_id}:{dock_sn}:{job_id} |
| | | double time = waylineRedisService.getConditionalWaylineJobTime(jobKey); |
| | | long now = System.currentTimeMillis(); |
| | | // prepare the task one day in advance. |
| | | int offset = 86_400_000; |
| | | |
| | | if (now + offset < time) { |
| | | return; |
| | | } |
| | | |
| | | WaylineJobDTO job = WaylineJobDTO.builder() |
| | | .jobId(jobKey.getJobId()) |
| | | .status(WaylineJobStatusEnum.FAILED.getVal()) |
| | | .executeTime(LocalDateTime.now()) |
| | | .completedTime(LocalDateTime.now()) |
| | | .code(HttpStatus.SC_INTERNAL_SERVER_ERROR).build(); |
| | | try { |
| | | Optional<WaylineJobDTO> waylineJobOpt = waylineRedisService.getConditionalWaylineJob(jobKey.getJobId()); |
| | | if (waylineJobOpt.isEmpty()) { |
| | | job.setCode(CommonErrorEnum.REDIS_DATA_NOT_FOUND.getErrorCode()); |
| | | waylineJobService.updateJob(job); |
| | | waylineRedisService.removePrepareConditionalWaylineJob(jobKey); |
| | | return; |
| | | } |
| | | WaylineJobDTO waylineJob = waylineJobOpt.get(); |
| | | |
| | | ResponseResult result = waylineJobService.publishOneFlightTask(waylineJob); |
| | | waylineRedisService.removePrepareConditionalWaylineJob(jobKey); |
| | | |
| | | if (ResponseResult.CODE_SUCCESS == result.getCode()) { |
| | | return; |
| | | } |
| | | |
| | | // If the end time is exceeded, no more retries will be made. |
| | | waylineRedisService.delConditionalWaylineJob(jobKey.getJobId()); |
| | | if (waylineJob.getEndTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() - RedisConst.WAYLINE_JOB_BLOCK_TIME * 1000 < now) { |
| | | return; |
| | | } |
| | | |
| | | // Retry if the end time has not been exceeded. |
| | | this.retryPrepareJob(jobKey, waylineJob); |
| | | |
| | | } catch (Exception e) { |
| | | log.info("Failed to prepare the conditional task."); |
| | | waylineJobService.updateJob(job); |
| | | } |
| | | |
| | | } |
| | | |
| | | private void retryPrepareJob(ConditionalWaylineJobKey jobKey, WaylineJobDTO waylineJob) { |
| | | Optional<WaylineJobDTO> childJobOpt = waylineJobService.createWaylineJobByParent(jobKey.getWorkspaceId(), jobKey.getJobId()); |
| | | if (childJobOpt.isEmpty()) { |
| | | log.error("Failed to create wayline job."); |
| | | return; |
| | | } |
| | | |
| | | WaylineJobDTO newJob = childJobOpt.get(); |
| | | newJob.setBeginTime(LocalDateTime.now().plusSeconds(RedisConst.WAYLINE_JOB_BLOCK_TIME)); |
| | | boolean isAdd = waylineRedisService.addPrepareConditionalWaylineJob(newJob); |
| | | if (!isAdd) { |
| | | log.error("Failed to create wayline job. {}", newJob.getJobId()); |
| | | return; |
| | | } |
| | | |
| | | waylineJob.setJobId(newJob.getJobId()); |
| | | waylineRedisService.setConditionalWaylineJob(waylineJob); |
| | | } |
| | | } |
| | |
| | | import com.dji.sample.wayline.model.dto.KmzFileProperties; |
| | | import com.dji.sample.wayline.model.dto.WaylineFileDTO; |
| | | import com.dji.sample.wayline.model.entity.WaylineFileEntity; |
| | | import com.dji.sample.wayline.model.enums.WaylineTemplateTypeEnum; |
| | | import com.dji.sample.wayline.model.param.WaylineQueryParam; |
| | | import com.dji.sample.wayline.service.IWaylineFileService; |
| | | import org.dom4j.Document; |
| | |
| | | |
| | | ZipEntry nextEntry = unzipFile.getNextEntry(); |
| | | while (Objects.nonNull(nextEntry)) { |
| | | boolean isWaylines = (KmzFileProperties.FILE_DIR_FIRST + "/" + KmzFileProperties.FILE_DIR_SECOND_WAYLINES).equals(nextEntry.getName()); |
| | | boolean isWaylines = (KmzFileProperties.FILE_DIR_FIRST + "/" + KmzFileProperties.FILE_DIR_SECOND_TEMPLATE).equals(nextEntry.getName()); |
| | | if (!isWaylines) { |
| | | nextEntry = unzipFile.getNextEntry(); |
| | | continue; |
| | |
| | | String subType = droneNode.valueOf(KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_DRONE_SUB_ENUM_VALUE); |
| | | String payloadType = payloadNode.valueOf(KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_PAYLOAD_ENUM_VALUE); |
| | | String payloadSubType = payloadNode.valueOf(KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_PAYLOAD_SUB_ENUM_VALUE); |
| | | String templateId = document.valueOf("//" + KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_TEMPLATE_ID); |
| | | String templateType = document.valueOf("//" + KmzFileProperties.TAG_WPML_PREFIX + KmzFileProperties.TAG_TEMPLATE_TYPE); |
| | | |
| | | if (!StringUtils.hasText(type) || !StringUtils.hasText(subType) || |
| | | !StringUtils.hasText(payloadSubType) || !StringUtils.hasText(payloadType) || |
| | | !StringUtils.hasText(templateId)) { |
| | | !StringUtils.hasText(templateType)) { |
| | | throw new RuntimeException("The file format is incorrect."); |
| | | } |
| | | |
| | |
| | | .objectKey(OssConfiguration.objectDirPrefix + File.separator + filename) |
| | | .name(filename.substring(0, filename.lastIndexOf(WAYLINE_FILE_SUFFIX))) |
| | | .sign(DigestUtils.md5DigestAsHex(file.getInputStream())) |
| | | .templateTypes(List.of(Integer.parseInt(templateId))) |
| | | .templateTypes(List.of(WaylineTemplateTypeEnum.find(templateType).map(WaylineTemplateTypeEnum::getVal).orElse(-1))) |
| | | .build()); |
| | | } |
| | | |
| | |
| | | import com.dji.sample.component.mqtt.service.IMessageSenderService; |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import com.dji.sample.component.redis.RedisOpsUtils; |
| | | import com.dji.sample.control.model.param.DrcModeParam; |
| | | import com.dji.sample.control.service.IDrcService; |
| | | import com.dji.sample.manage.model.dto.DeviceDTO; |
| | | import com.dji.sample.manage.model.enums.DeviceModeCodeEnum; |
| | | import com.dji.sample.manage.model.enums.DockModeCodeEnum; |
| | | import com.dji.sample.manage.model.receiver.OsdDockReceiver; |
| | | import com.dji.sample.manage.model.receiver.OsdSubDeviceReceiver; |
| | | import com.dji.sample.manage.service.IDeviceRedisService; |
| | | import com.dji.sample.manage.service.IDeviceService; |
| | | import com.dji.sample.media.model.MediaFileCountDTO; |
| | | import com.dji.sample.media.model.MediaMethodEnum; |
| | |
| | | import com.dji.sample.wayline.dao.IWaylineJobMapper; |
| | | import com.dji.sample.wayline.model.dto.*; |
| | | import com.dji.sample.wayline.model.entity.WaylineJobEntity; |
| | | import com.dji.sample.wayline.model.enums.WaylineErrorCodeEnum; |
| | | import com.dji.sample.wayline.model.enums.WaylineJobStatusEnum; |
| | | import com.dji.sample.wayline.model.enums.WaylineMethodEnum; |
| | | import com.dji.sample.wayline.model.enums.WaylineTaskTypeEnum; |
| | | import com.dji.sample.wayline.model.param.CreateJobParam; |
| | | import com.dji.sample.wayline.model.param.UpdateJobParam; |
| | | import com.dji.sample.wayline.service.IWaylineFileService; |
| | | import com.dji.sample.wayline.service.IWaylineJobService; |
| | | import com.dji.sample.wayline.service.IWaylineRedisService; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | import org.springframework.transaction.annotation.Isolation; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.util.CollectionUtils; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.net.URL; |
| | | import java.sql.SQLException; |
| | |
| | | @Autowired |
| | | private IFileService fileService; |
| | | |
| | | @Autowired |
| | | private IDrcService drcService; |
| | | |
| | | @Autowired |
| | | private IDeviceRedisService deviceRedisService; |
| | | |
| | | @Autowired |
| | | private IWaylineRedisService waylineRedisService; |
| | | |
| | | private Optional<WaylineJobDTO> insertWaylineJob(WaylineJobEntity jobEntity) { |
| | | int id = mapper.insert(jobEntity); |
| | | if (id <= 0) { |
| | |
| | | .beginTime(beginTime) |
| | | .endTime(endTime) |
| | | .status(WaylineJobStatusEnum.PENDING.getVal()) |
| | | .taskType(param.getTaskType()) |
| | | .waylineType(param.getWaylineType()) |
| | | .taskType(param.getTaskType().getVal()) |
| | | .waylineType(param.getWaylineType().getVal()) |
| | | .outOfControlAction(param.getOutOfControlAction()) |
| | | .rthAltitude(param.getRthAltitude()) |
| | | .mediaCount(0) |
| | |
| | | return this.insertWaylineJob(jobEntity); |
| | | } |
| | | |
| | | /** |
| | | * For immediate tasks, the server time shall prevail. |
| | | * @param param |
| | | */ |
| | | private void fillImmediateTime(CreateJobParam param) { |
| | | if (WaylineTaskTypeEnum.IMMEDIATE != param.getTaskType()) { |
| | | return; |
| | | } |
| | | long now = System.currentTimeMillis() / 1000; |
| | | if (CollectionUtils.isEmpty(param.getTaskDays())) { |
| | | param.setTaskDays(List.of(now)); |
| | | } |
| | | if (CollectionUtils.isEmpty(param.getTaskPeriods())) { |
| | | param.setTaskPeriods(List.of(List.of(now))); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public ResponseResult publishFlightTask(CreateJobParam param, CustomClaim customClaim) throws SQLException { |
| | | if (WaylineTaskTypeEnum.IMMEDIATE.getVal() == param.getTaskType()) { |
| | | param.setExecuteTime(System.currentTimeMillis()); |
| | | } |
| | | Optional<WaylineJobDTO> waylineJobOpt = this.createWaylineJob(param, |
| | | customClaim.getWorkspaceId(), customClaim.getUsername(), |
| | | param.getExecuteTime(), param.getExecuteTime()); |
| | | fillImmediateTime(param); |
| | | |
| | | for (Long taskDay : param.getTaskDays()) { |
| | | LocalDate date = LocalDate.ofInstant(Instant.ofEpochSecond(taskDay), ZoneId.systemDefault()); |
| | | for (List<Long> taskPeriod : param.getTaskPeriods()) { |
| | | long beginTime = LocalDateTime.of(date, LocalTime.ofInstant(Instant.ofEpochSecond(taskPeriod.get(0)), ZoneId.systemDefault())) |
| | | .atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); |
| | | long endTime = taskPeriod.size() > 1 ? |
| | | LocalDateTime.of(date, LocalTime.ofInstant(Instant.ofEpochSecond(taskPeriod.get(1)), ZoneId.systemDefault())) |
| | | .atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() : beginTime; |
| | | Optional<WaylineJobDTO> waylineJobOpt = this.createWaylineJob(param, customClaim.getWorkspaceId(), customClaim.getUsername(), beginTime, endTime); |
| | | if (waylineJobOpt.isEmpty()) { |
| | | throw new SQLException("Failed to create wayline job."); |
| | | } |
| | | WaylineJobDTO waylineJob = waylineJobOpt.get(); |
| | | |
| | | boolean isOnline = deviceService.checkDeviceOnline(waylineJob.getDockSn()); |
| | | WaylineJobDTO waylineJob = waylineJobOpt.get(); |
| | | // If it is a conditional task type, add conditions to the job parameters. |
| | | addConditions(waylineJob, param, beginTime, endTime); |
| | | |
| | | return this.publishOneFlightTask(waylineJob); |
| | | } |
| | | } |
| | | return ResponseResult.error(); |
| | | } |
| | | |
| | | private void addConditions(WaylineJobDTO waylineJob, CreateJobParam param, Long beginTime, Long endTime) { |
| | | if (WaylineTaskTypeEnum.CONDITION != param.getTaskType()) { |
| | | return; |
| | | } |
| | | |
| | | waylineJob.setConditions( |
| | | WaylineTaskConditionDTO.builder() |
| | | .executableConditions(Objects.nonNull(param.getMinStorageCapacity()) ? |
| | | WaylineTaskExecutableConditionDTO.builder().storageCapacity(param.getMinStorageCapacity()).build() : null) |
| | | .readyConditions(WaylineTaskReadyConditionDTO.builder() |
| | | .batteryCapacity(param.getMinBatteryCapacity()) |
| | | .beginTime(beginTime) |
| | | .endTime(endTime) |
| | | .build()) |
| | | .build()); |
| | | |
| | | waylineRedisService.setConditionalWaylineJob(waylineJob); |
| | | // key: wayline_job_condition, value: {workspace_id}:{dock_sn}:{job_id} |
| | | boolean isAdd = waylineRedisService.addPrepareConditionalWaylineJob(waylineJob); |
| | | if (!isAdd) { |
| | | throw new RuntimeException("Failed to create conditional job."); |
| | | } |
| | | } |
| | | |
| | | public ResponseResult publishOneFlightTask(WaylineJobDTO waylineJob) throws SQLException { |
| | | |
| | | boolean isOnline = deviceRedisService.checkDeviceOnline(waylineJob.getDockSn()); |
| | | if (!isOnline) { |
| | | throw new RuntimeException("Dock is offline."); |
| | | } |
| | | |
| | | boolean isSuccess = this.prepareFlightTask(waylineJob); |
| | | if (!isSuccess) { |
| | | return ResponseResult.error("Failed to prepare job."); |
| | | } |
| | | |
| | | // Issue an immediate task execution command. |
| | | if (WaylineTaskTypeEnum.IMMEDIATE.getVal() == waylineJob.getTaskType()) { |
| | | if (!executeFlightTask(waylineJob.getWorkspaceId(), waylineJob.getJobId())) { |
| | | return ResponseResult.error("Failed to execute job."); |
| | | } |
| | | } |
| | | |
| | | if (WaylineTaskTypeEnum.TIMED.getVal() == waylineJob.getTaskType()) { |
| | | // key: wayline_job_timed, value: {workspace_id}:{dock_sn}:{job_id} |
| | | boolean isAdd = RedisOpsUtils.zAdd(RedisConst.WAYLINE_JOB_TIMED_EXECUTE, |
| | | waylineJob.getWorkspaceId() + RedisConst.DELIMITER + waylineJob.getDockSn() + RedisConst.DELIMITER + waylineJob.getJobId(), |
| | | waylineJob.getBeginTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); |
| | | if (!isAdd) { |
| | | return ResponseResult.error("Failed to create scheduled job."); |
| | | } |
| | | } |
| | | |
| | | return ResponseResult.success(); |
| | | } |
| | | |
| | | private Boolean prepareFlightTask(WaylineJobDTO waylineJob) throws SQLException { |
| | | // get wayline file |
| | | Optional<WaylineFileDTO> waylineFile = waylineFileService.getWaylineByWaylineId(waylineJob.getWorkspaceId(), waylineJob.getFileId()); |
| | | if (waylineFile.isEmpty()) { |
| | |
| | | .build()) |
| | | .build(); |
| | | |
| | | String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + |
| | | waylineJob.getDockSn() + TopicConst.SERVICES_SUF; |
| | | CommonTopicResponse<Object> response = CommonTopicResponse.builder() |
| | | .tid(UUID.randomUUID().toString()) |
| | | .bid(waylineJob.getJobId()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data(flightTask) |
| | | .method(WaylineMethodEnum.FLIGHT_TASK_PREPARE.getMethod()) |
| | | .build(); |
| | | if (WaylineTaskTypeEnum.CONDITION.getVal() == waylineJob.getTaskType()) { |
| | | if (Objects.isNull(waylineJob.getConditions())) { |
| | | throw new IllegalArgumentException(); |
| | | } |
| | | flightTask.setReadyConditions(waylineJob.getConditions().getReadyConditions()); |
| | | flightTask.setExecutableConditions(waylineJob.getConditions().getExecutableConditions()); |
| | | } |
| | | |
| | | ServiceReply serviceReply = messageSender.publishWithReply(topic, response); |
| | | ServiceReply serviceReply = messageSender.publishServicesTopic( |
| | | waylineJob.getDockSn(), WaylineMethodEnum.FLIGHT_TASK_PREPARE.getMethod(), flightTask, waylineJob.getJobId()); |
| | | if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) { |
| | | log.info("Prepare task ====> Error code: {}", serviceReply.getResult()); |
| | | this.updateJob(WaylineJobDTO.builder() |
| | |
| | | .status(WaylineJobStatusEnum.FAILED.getVal()) |
| | | .completedTime(LocalDateTime.now()) |
| | | .code(serviceReply.getResult()).build()); |
| | | return ResponseResult.error("Prepare task ====> Error code: " + serviceReply.getResult()); |
| | | return false; |
| | | } |
| | | |
| | | // Issue an immediate task execution command. |
| | | if (WaylineTaskTypeEnum.IMMEDIATE.getVal() == waylineJob.getTaskType()) { |
| | | if (!executeFlightTask(waylineJob.getWorkspaceId(), waylineJob.getJobId())) { |
| | | return ResponseResult.error("Failed to execute job."); |
| | | } |
| | | } |
| | | |
| | | if (WaylineTaskTypeEnum.TIMED.getVal() == waylineJob.getTaskType()) { |
| | | boolean isAdd = RedisOpsUtils.zAdd(RedisConst.WAYLINE_JOB_TIMED_EXECUTE, |
| | | waylineJob.getWorkspaceId() + RedisConst.DELIMITER + waylineJob.getDockSn() + RedisConst.DELIMITER + waylineJob.getJobId(), |
| | | waylineJob.getBeginTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); |
| | | if (!isAdd) { |
| | | return ResponseResult.error("Failed to create scheduled job."); |
| | | } |
| | | } |
| | | |
| | | return ResponseResult.success(); |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | |
| | | throw new IllegalArgumentException("Job doesn't exist."); |
| | | } |
| | | |
| | | boolean isOnline = deviceService.checkDeviceOnline(waylineJob.get().getDockSn()); |
| | | boolean isOnline = deviceRedisService.checkDeviceOnline(waylineJob.get().getDockSn()); |
| | | if (!isOnline) { |
| | | throw new RuntimeException("Dock is offline."); |
| | | } |
| | |
| | | WaylineJobDTO job = waylineJob.get(); |
| | | WaylineTaskCreateDTO flightTask = WaylineTaskCreateDTO.builder().flightId(jobId).build(); |
| | | |
| | | String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + |
| | | job.getDockSn() + TopicConst.SERVICES_SUF; |
| | | CommonTopicResponse<Object> response = CommonTopicResponse.builder() |
| | | .tid(UUID.randomUUID().toString()) |
| | | .bid(jobId) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data(flightTask) |
| | | .method(WaylineMethodEnum.FLIGHT_TASK_EXECUTE.getMethod()) |
| | | .build(); |
| | | |
| | | ServiceReply serviceReply = messageSender.publishWithReply(topic, response); |
| | | ServiceReply serviceReply = messageSender.publishServicesTopic( |
| | | job.getDockSn(), WaylineMethodEnum.FLIGHT_TASK_EXECUTE.getMethod(), flightTask, jobId); |
| | | if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) { |
| | | log.info("Execute job ====> Error code: {}", serviceReply.getResult()); |
| | | this.updateJob(WaylineJobDTO.builder() |
| | |
| | | .status(WaylineJobStatusEnum.FAILED.getVal()) |
| | | .completedTime(LocalDateTime.now()) |
| | | .code(serviceReply.getResult()).build()); |
| | | // The conditional task fails and enters the blocking status. |
| | | if (WaylineTaskTypeEnum.CONDITION.getVal() == job.getTaskType() |
| | | && WaylineErrorCodeEnum.find(serviceReply.getResult()).isBlock()) { |
| | | waylineRedisService.setBlockedWaylineJob(job.getDockSn(), jobId); |
| | | } |
| | | return false; |
| | | } |
| | | |
| | |
| | | .executeTime(LocalDateTime.now()) |
| | | .status(WaylineJobStatusEnum.IN_PROGRESS.getVal()) |
| | | .build()); |
| | | RedisOpsUtils.setWithExpire(RedisConst.WAYLINE_JOB_RUNNING_PREFIX + job.getDockSn(), |
| | | EventsReceiver.<WaylineTaskProgressReceiver>builder().bid(jobId).sn(job.getDockSn()).build(), |
| | | RedisConst.DEVICE_ALIVE_SECOND * RedisConst.DEVICE_ALIVE_SECOND); |
| | | waylineRedisService.setRunningWaylineJob(job.getDockSn(), EventsReceiver.<WaylineTaskProgressReceiver>builder().bid(jobId).sn(job.getDockSn()).build()); |
| | | return true; |
| | | } |
| | | |
| | |
| | | // Check if the task status is correct. |
| | | boolean isErr = !jobIds.removeAll(waylineJobIds) || !jobIds.isEmpty() ; |
| | | if (isErr) { |
| | | throw new IllegalArgumentException("These tasks have an incorrect status and cannot be canceled. " |
| | | + Arrays.toString(jobIds.toArray())); |
| | | throw new IllegalArgumentException("These tasks have an incorrect status and cannot be canceled. " + Arrays.toString(jobIds.toArray())); |
| | | } |
| | | |
| | | // Group job id by dock sn. |
| | |
| | | } |
| | | |
| | | public void publishCancelTask(String workspaceId, String dockSn, List<String> jobIds) { |
| | | boolean isOnline = deviceService.checkDeviceOnline(dockSn); |
| | | boolean isOnline = deviceRedisService.checkDeviceOnline(dockSn); |
| | | if (!isOnline) { |
| | | throw new RuntimeException("Dock is offline."); |
| | | } |
| | | String topic = TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + dockSn + TopicConst.SERVICES_SUF; |
| | | |
| | | CommonTopicResponse<Object> response = CommonTopicResponse.builder() |
| | | .tid(UUID.randomUUID().toString()) |
| | | .bid(UUID.randomUUID().toString()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .data(Map.of(MapKeyConst.FLIGHT_IDS, jobIds)) |
| | | .method(WaylineMethodEnum.FLIGHT_TASK_CANCEL.getMethod()) |
| | | .build(); |
| | | |
| | | ServiceReply serviceReply = messageSender.publishWithReply(topic, response); |
| | | ServiceReply serviceReply = messageSender.publishServicesTopic( |
| | | dockSn, WaylineMethodEnum.FLIGHT_TASK_CANCEL.getMethod(), Map.of(MapKeyConst.FLIGHT_IDS, jobIds)); |
| | | if (ResponseResult.CODE_SUCCESS != serviceReply.getResult()) { |
| | | log.info("Cancel job ====> Error code: {}", serviceReply.getResult()); |
| | | throw new RuntimeException("Failed to cancel the wayline job of " + dockSn); |
| | |
| | | |
| | | String topic = headers.get(MqttHeaders.RECEIVED_TOPIC).toString() + TopicConst._REPLY_SUF; |
| | | |
| | | DeviceDTO device = (DeviceDTO) RedisOpsUtils.get(RedisConst.DEVICE_ONLINE_PREFIX + receiver.getGateway()); |
| | | Optional<WaylineJobDTO> waylineJobOpt = this.getJobByJobId(device.getWorkspaceId(), jobId); |
| | | Optional<DeviceDTO> deviceOpt = deviceRedisService.getDeviceOnline(receiver.getGateway()); |
| | | if (deviceOpt.isEmpty()) { |
| | | return; |
| | | } |
| | | Optional<WaylineJobDTO> waylineJobOpt = this.getJobByJobId(deviceOpt.get().getWorkspaceId(), jobId); |
| | | if (waylineJobOpt.isEmpty()) { |
| | | builder.data(RequestsReply.error(CommonErrorEnum.ILLEGAL_ARGUMENT)); |
| | | messageSender.publish(topic, builder.build()); |
| | |
| | | |
| | | String dockSn = jobOpt.get().getDockSn(); |
| | | String key = RedisConst.MEDIA_HIGHEST_PRIORITY_PREFIX + dockSn; |
| | | if (RedisOpsUtils.checkExist(key) && |
| | | jobId.equals(((MediaFileCountDTO) RedisOpsUtils.get(key)).getJobId())) { |
| | | if (RedisOpsUtils.checkExist(key) && jobId.equals(((MediaFileCountDTO) RedisOpsUtils.get(key)).getJobId())) { |
| | | return; |
| | | } |
| | | |
| | | ServiceReply reply = messageSender.publishWithReply(TopicConst.THING_MODEL_PRE + TopicConst.PRODUCT + dockSn + TopicConst.SERVICES_SUF, |
| | | CommonTopicResponse.builder() |
| | | .tid(UUID.randomUUID().toString()) |
| | | .bid(UUID.randomUUID().toString()) |
| | | .timestamp(System.currentTimeMillis()) |
| | | .method(MediaMethodEnum.UPLOAD_FLIGHT_TASK_MEDIA_PRIORITIZE.getMethod()) |
| | | .data(Map.of(MapKeyConst.FLIGHT_ID, jobId)) |
| | | .build()); |
| | | ServiceReply reply = messageSender.publishServicesTopic( |
| | | dockSn, MediaMethodEnum.UPLOAD_FLIGHT_TASK_MEDIA_PRIORITIZE.getMethod(), Map.of(MapKeyConst.FLIGHT_ID, jobId)); |
| | | if (ResponseResult.CODE_SUCCESS != reply.getResult()) { |
| | | throw new RuntimeException("Failed to set media job upload priority. Error Code: " + reply.getResult()); |
| | | } |
| | |
| | | .build(); |
| | | } |
| | | |
| | | @Override |
| | | public void updateJobStatus(String workspaceId, String jobId, UpdateJobParam param) { |
| | | Optional<WaylineJobDTO> waylineJobOpt = this.getJobByJobId(workspaceId, jobId); |
| | | if (waylineJobOpt.isEmpty()) { |
| | | throw new RuntimeException("The job does not exist."); |
| | | } |
| | | WaylineJobDTO waylineJob = waylineJobOpt.get(); |
| | | WaylineJobStatusEnum statusEnum = this.getWaylineState(waylineJob.getDockSn()); |
| | | if (statusEnum.getEnd() || WaylineJobStatusEnum.PENDING == statusEnum) { |
| | | throw new RuntimeException("The wayline job status does not match, and the operation cannot be performed."); |
| | | } |
| | | |
| | | switch (param.getStatus()) { |
| | | case PAUSE: |
| | | pauseJob(workspaceId, waylineJob.getDockSn(), jobId, statusEnum); |
| | | break; |
| | | case RESUME: |
| | | resumeJob(workspaceId, waylineJob.getDockSn(), jobId, statusEnum); |
| | | break; |
| | | } |
| | | |
| | | } |
| | | |
| | | public WaylineJobStatusEnum getWaylineState(String dockSn) { |
| | | Optional<DeviceDTO> dockOpt = deviceRedisService.getDeviceOnline(dockSn); |
| | | if (dockOpt.isEmpty() || !StringUtils.hasText(dockOpt.get().getChildDeviceSn())) { |
| | | return WaylineJobStatusEnum.UNKNOWN; |
| | | } |
| | | Optional<OsdDockReceiver> dockOsdOpt = deviceRedisService.getDeviceOsd(dockSn, OsdDockReceiver.class); |
| | | Optional<OsdSubDeviceReceiver> deviceOsdOpt = deviceRedisService.getDeviceOsd(dockOpt.get().getChildDeviceSn(), OsdSubDeviceReceiver.class); |
| | | if (dockOsdOpt.isEmpty() || deviceOsdOpt.isEmpty() || DockModeCodeEnum.WORKING != dockOsdOpt.get().getModeCode()) { |
| | | return WaylineJobStatusEnum.UNKNOWN; |
| | | } |
| | | |
| | | OsdSubDeviceReceiver osdDevice = deviceOsdOpt.get(); |
| | | if (DeviceModeCodeEnum.WAYLINE == osdDevice.getModeCode() |
| | | || DeviceModeCodeEnum.MANUAL == osdDevice.getModeCode() |
| | | || DeviceModeCodeEnum.TAKEOFF_AUTO == osdDevice.getModeCode()) { |
| | | if (StringUtils.hasText(waylineRedisService.getPausedWaylineJobId(dockSn))) { |
| | | return WaylineJobStatusEnum.PAUSED; |
| | | } |
| | | if (waylineRedisService.getRunningWaylineJob(dockSn).isPresent()) { |
| | | return WaylineJobStatusEnum.IN_PROGRESS; |
| | | } |
| | | } |
| | | return WaylineJobStatusEnum.UNKNOWN; |
| | | } |
| | | |
| | | private void pauseJob(String workspaceId, String dockSn, String jobId, WaylineJobStatusEnum statusEnum) { |
| | | if (WaylineJobStatusEnum.PAUSED == statusEnum && jobId.equals(waylineRedisService.getPausedWaylineJobId(dockSn))) { |
| | | waylineRedisService.setPausedWaylineJob(dockSn, jobId); |
| | | return; |
| | | } |
| | | |
| | | ServiceReply reply = messageSender.publishServicesTopic( |
| | | dockSn, WaylineMethodEnum.FLIGHT_TASK_PAUSE.getMethod(), "", jobId); |
| | | if (ResponseResult.CODE_SUCCESS != reply.getResult()) { |
| | | throw new RuntimeException("Failed to pause wayline job. Error Code: " + reply.getResult()); |
| | | } |
| | | waylineRedisService.delRunningWaylineJob(dockSn); |
| | | waylineRedisService.setPausedWaylineJob(dockSn, jobId); |
| | | } |
| | | |
| | | private void resumeJob(String workspaceId, String dockSn, String jobId, WaylineJobStatusEnum statusEnum) { |
| | | Optional<EventsReceiver<WaylineTaskProgressReceiver>> runningDataOpt = waylineRedisService.getRunningWaylineJob(dockSn); |
| | | if (WaylineJobStatusEnum.IN_PROGRESS == statusEnum && jobId.equals(runningDataOpt.map(EventsReceiver::getSn).get())) { |
| | | waylineRedisService.setRunningWaylineJob(dockSn, runningDataOpt.get()); |
| | | return; |
| | | } |
| | | ServiceReply reply = messageSender.publishServicesTopic( |
| | | dockSn, WaylineMethodEnum.FLIGHT_TASK_RESUME.getMethod(), "", jobId); |
| | | if (ResponseResult.CODE_SUCCESS != reply.getResult()) { |
| | | throw new RuntimeException("Failed to resume wayline job. Error Code: " + reply.getResult()); |
| | | } |
| | | |
| | | runningDataOpt.ifPresent(runningData -> waylineRedisService.setRunningWaylineJob(dockSn, runningData)); |
| | | waylineRedisService.delPausedWaylineJob(dockSn); |
| | | |
| | | if (deviceService.checkDockDrcMode(dockSn)) { |
| | | drcService.deviceDrcExit(workspaceId, DrcModeParam.builder().dockSn(dockSn) |
| | | .clientId(drcService.getDrcModeInRedis(dockSn)).build()); |
| | | } |
| | | |
| | | } |
| | | |
| | | private WaylineJobDTO entity2Dto(WaylineJobEntity entity) { |
| | | if (entity == null) { |
| | | return null; |
| | |
| | | .username(entity.getUsername()) |
| | | .workspaceId(entity.getWorkspaceId()) |
| | | .status(WaylineJobStatusEnum.IN_PROGRESS.getVal() == entity.getStatus() && |
| | | RedisOpsUtils.checkExist(RedisConst.WAYLINE_JOB_PAUSED_PREFIX + entity.getJobId()) ? |
| | | entity.getJobId().equals(waylineRedisService.getPausedWaylineJobId(entity.getDockSn())) ? |
| | | WaylineJobStatusEnum.PAUSED.getVal() : entity.getStatus()) |
| | | .code(entity.getErrorCode()) |
| | | .beginTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getBeginTime()), ZoneId.systemDefault())) |
| | |
| | | if (Objects.nonNull(entity.getEndTime())) { |
| | | builder.endTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(entity.getEndTime()), ZoneId.systemDefault())); |
| | | } |
| | | if (WaylineJobStatusEnum.IN_PROGRESS.getVal() == entity.getStatus() && RedisOpsUtils.getExpire(entity.getJobId()) > 0) { |
| | | EventsReceiver<WaylineTaskProgressReceiver> taskProgress = (EventsReceiver<WaylineTaskProgressReceiver>) RedisOpsUtils.get(RedisConst.WAYLINE_JOB_RUNNING_PREFIX + entity.getDockSn()); |
| | | if (Objects.nonNull(taskProgress.getOutput()) && Objects.nonNull(taskProgress.getOutput().getProgress())) { |
| | | builder.progress(taskProgress.getOutput().getProgress().getPercent()); |
| | | } |
| | | if (WaylineJobStatusEnum.IN_PROGRESS.getVal() == entity.getStatus()) { |
| | | builder.progress(waylineRedisService.getRunningWaylineJob(entity.getDockSn()) |
| | | .map(EventsReceiver::getOutput) |
| | | .map(WaylineTaskProgressReceiver::getProgress) |
| | | .map(WaylineTaskProgress::getPercent) |
| | | .orElse(null)); |
| | | } |
| | | |
| | | if (entity.getMediaCount() == 0) { |
| New file |
| | |
| | | package com.dji.sample.wayline.service.impl; |
| | | |
| | | import com.dji.sample.component.mqtt.model.EventsReceiver; |
| | | import com.dji.sample.component.redis.RedisConst; |
| | | import com.dji.sample.component.redis.RedisOpsUtils; |
| | | import com.dji.sample.wayline.model.dto.ConditionalWaylineJobKey; |
| | | import com.dji.sample.wayline.model.dto.WaylineJobDTO; |
| | | import com.dji.sample.wayline.model.dto.WaylineTaskProgressReceiver; |
| | | import com.dji.sample.wayline.service.IWaylineRedisService; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.time.Duration; |
| | | import java.time.LocalDateTime; |
| | | import java.time.ZoneId; |
| | | import java.util.Objects; |
| | | import java.util.Optional; |
| | | |
| | | /** |
| | | * @author sean |
| | | * @version 1.4 |
| | | * @date 2023/3/24 |
| | | */ |
| | | @Service |
| | | public class WaylineRedisServiceImpl implements IWaylineRedisService { |
| | | |
| | | @Override |
| | | public void setRunningWaylineJob(String dockSn, EventsReceiver<WaylineTaskProgressReceiver> data) { |
| | | RedisOpsUtils.setWithExpire(RedisConst.WAYLINE_JOB_RUNNING_PREFIX + dockSn, data, RedisConst.DRC_MODE_ALIVE_SECOND); |
| | | } |
| | | |
| | | @Override |
| | | public Optional<EventsReceiver<WaylineTaskProgressReceiver>> getRunningWaylineJob(String dockSn) { |
| | | return Optional.ofNullable((EventsReceiver<WaylineTaskProgressReceiver>) RedisOpsUtils.get(RedisConst.WAYLINE_JOB_RUNNING_PREFIX + dockSn)); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean delRunningWaylineJob(String dockSn) { |
| | | return RedisOpsUtils.del(RedisConst.WAYLINE_JOB_RUNNING_PREFIX + dockSn); |
| | | } |
| | | |
| | | @Override |
| | | public void setPausedWaylineJob(String dockSn, String jobId) { |
| | | RedisOpsUtils.setWithExpire(RedisConst.WAYLINE_JOB_PAUSED_PREFIX + dockSn, jobId, RedisConst.DRC_MODE_ALIVE_SECOND); |
| | | } |
| | | |
| | | @Override |
| | | public String getPausedWaylineJobId(String dockSn) { |
| | | return (String) RedisOpsUtils.get(RedisConst.WAYLINE_JOB_PAUSED_PREFIX + dockSn); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean delPausedWaylineJob(String dockSn) { |
| | | return RedisOpsUtils.del(RedisConst.WAYLINE_JOB_PAUSED_PREFIX + dockSn); |
| | | } |
| | | |
| | | @Override |
| | | public void setBlockedWaylineJob(String dockSn, String jobId) { |
| | | RedisOpsUtils.setWithExpire(RedisConst.WAYLINE_JOB_BLOCK_PREFIX + dockSn, jobId, RedisConst.WAYLINE_JOB_BLOCK_TIME); |
| | | } |
| | | |
| | | @Override |
| | | public String getBlockedWaylineJobId(String dockSn) { |
| | | return (String) RedisOpsUtils.get(RedisConst.WAYLINE_JOB_BLOCK_PREFIX + dockSn); |
| | | } |
| | | |
| | | @Override |
| | | public void setConditionalWaylineJob(WaylineJobDTO waylineJob) { |
| | | if (!StringUtils.hasText(waylineJob.getJobId())) { |
| | | throw new RuntimeException("Job id can't be null."); |
| | | } |
| | | RedisOpsUtils.setWithExpire(RedisConst.WAYLINE_JOB_CONDITION_PREFIX + waylineJob.getJobId(), waylineJob, |
| | | (Duration.between(waylineJob.getEndTime(), LocalDateTime.now()).getSeconds())); |
| | | } |
| | | |
| | | @Override |
| | | public Optional<WaylineJobDTO> getConditionalWaylineJob(String jobId) { |
| | | return Optional.ofNullable((WaylineJobDTO) RedisOpsUtils.get(RedisConst.WAYLINE_JOB_CONDITION_PREFIX + jobId)); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean delConditionalWaylineJob(String jobId) { |
| | | return RedisOpsUtils.del(RedisConst.WAYLINE_JOB_CONDITION_PREFIX + jobId); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean addPrepareConditionalWaylineJob(WaylineJobDTO waylineJob) { |
| | | if (Objects.isNull(waylineJob.getBeginTime())) { |
| | | return false; |
| | | } |
| | | // value: {workspace_id}:{dock_sn}:{job_id} |
| | | return RedisOpsUtils.zAdd(RedisConst.WAYLINE_JOB_CONDITION_PREPARE, |
| | | waylineJob.getWorkspaceId() + RedisConst.DELIMITER + waylineJob.getDockSn() + RedisConst.DELIMITER + waylineJob.getJobId(), |
| | | waylineJob.getBeginTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); |
| | | } |
| | | |
| | | @Override |
| | | public Optional<ConditionalWaylineJobKey> getNearestConditionalWaylineJob() { |
| | | return Optional.ofNullable(RedisOpsUtils.zGetMin(RedisConst.WAYLINE_JOB_CONDITION_PREPARE)) |
| | | .map(Object::toString).map(ConditionalWaylineJobKey::new); |
| | | } |
| | | |
| | | @Override |
| | | public Double getConditionalWaylineJobTime(ConditionalWaylineJobKey jobKey) { |
| | | return RedisOpsUtils.zScore(RedisConst.WAYLINE_JOB_CONDITION_PREPARE, jobKey.getKey()); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean removePrepareConditionalWaylineJob(ConditionalWaylineJobKey jobKey) { |
| | | return RedisOpsUtils.zRemove(RedisConst.WAYLINE_JOB_CONDITION_PREPARE, jobKey.getKey()); |
| | | } |
| | | } |
| | |
| | | path: |
| | | # Topics that need to be subscribed when initially connecting to mqtt, multiple topics are divided by ",". |
| | | inbound-topic: sys/product/+/status,thing/product/+/requests |
| | | DRC: |
| | | protocol: WS # @see com.dji.sample.component.mqtt.model.MqttProtocolEnum |
| | | host: Please enter your ip. |
| | | port: 8083 |
| | | path: /mqtt |
| | | |
| | | url: |
| | | manage: |
| | |
| | | |
| | | # Tutorial: https://www.alibabacloud.com/help/en/object-storage-service/latest/use-a-temporary-credential-provided-by-sts-to-access-oss |
| | | oss: |
| | | enable: true |
| | | enable: false |
| | | provider: ali # @see com.dji.sample.component.OssConfiguration.model.enums.OssTypeEnum |
| | | endpoint: https://oss-cn-hangzhou.aliyuncs.com |
| | | access-key: Please enter your access key. |