464 changed files with 26134 additions and 1257 deletions
@ -0,0 +1,162 @@ |
|||||
|
{ |
||||
|
"ruleChain": { |
||||
|
"additionalInfo": null, |
||||
|
"name": "Edge Root Rule Chain", |
||||
|
"type": "EDGE", |
||||
|
"firstRuleNodeId": null, |
||||
|
"root": true, |
||||
|
"debugMode": false, |
||||
|
"configuration": null |
||||
|
}, |
||||
|
"metadata": { |
||||
|
"firstNodeIndex": 0, |
||||
|
"nodes": [ |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.", |
||||
|
"layoutX": 203, |
||||
|
"layoutY": 259 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", |
||||
|
"name": "Device Profile Node", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"persistAlarmRulesState": false, |
||||
|
"fetchAlarmRulesStateOnStart": false |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 823, |
||||
|
"layoutY": 157 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", |
||||
|
"name": "Save Timeseries", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"defaultTTL": 0 |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 824, |
||||
|
"layoutY": 52 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", |
||||
|
"name": "Save Client Attributes", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"scope": "CLIENT_SCOPE" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 347, |
||||
|
"layoutY": 149 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", |
||||
|
"name": "Message Type Switch", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"version": 0 |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 825, |
||||
|
"layoutY": 266 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.action.TbLogNode", |
||||
|
"name": "Log RPC from Device", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 824, |
||||
|
"layoutY": 378 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.action.TbLogNode", |
||||
|
"name": "Log Other", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 824, |
||||
|
"layoutY": 466 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", |
||||
|
"name": "RPC Call Request", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"timeoutInSeconds": 60 |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 1134, |
||||
|
"layoutY": 132 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode", |
||||
|
"name": "Push to cloud", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"version": 0 |
||||
|
} |
||||
|
} |
||||
|
], |
||||
|
"connections": [ |
||||
|
{ |
||||
|
"fromIndex": 0, |
||||
|
"toIndex": 3, |
||||
|
"type": "Success" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 1, |
||||
|
"toIndex": 7, |
||||
|
"type": "Success" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 2, |
||||
|
"toIndex": 7, |
||||
|
"type": "Success" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 3, |
||||
|
"toIndex": 6, |
||||
|
"type": "RPC Request to Device" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 3, |
||||
|
"toIndex": 5, |
||||
|
"type": "Other" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 3, |
||||
|
"toIndex": 2, |
||||
|
"type": "Post attributes" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 3, |
||||
|
"toIndex": 1, |
||||
|
"type": "Post telemetry" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 3, |
||||
|
"toIndex": 4, |
||||
|
"type": "RPC Request from Device" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 4, |
||||
|
"toIndex": 7, |
||||
|
"type": "Success" |
||||
|
} |
||||
|
], |
||||
|
"ruleChainConnections": null |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,25 @@ |
|||||
|
{ |
||||
|
"widgetsBundle": { |
||||
|
"alias": "edge_widgets", |
||||
|
"title": "Edge widgets", |
||||
|
"image": null |
||||
|
}, |
||||
|
"widgetTypes": [ |
||||
|
{ |
||||
|
"alias": "edges_overview", |
||||
|
"name": "Edge Quick Overview", |
||||
|
"descriptor": { |
||||
|
"type": "latest", |
||||
|
"sizeX": 7.5, |
||||
|
"sizeY": 5, |
||||
|
"resources": [], |
||||
|
"templateHtml": "<tb-edges-overview-widget \n [ctx]=\"ctx\">\n</tb-edges-overview-widget>", |
||||
|
"templateCss": "", |
||||
|
"controllerScript": "self.onInit = function() {\n};\n\nself.typeParameters = function() {\n return {\n maxDatasources: 1,\n dataKeysOptional: true\n };\n}\n\nself.onDestroy = function() {\n};\n", |
||||
|
"settingsSchema": "{\n \"schema\": {\n \"type\": \"object\",\n \"title\": \"EdgeOverviewSettings\",\n \"properties\": {\n \"enableDefaultTitle\": {\n \"title\": \"Display default title\",\n \"type\": \"boolean\",\n \"default\": true\n }\n },\n \"required\": []\n },\n \"form\": [\n \"enableDefaultTitle\"\n ]\n}", |
||||
|
"dataKeySettingsSchema": "{}\n", |
||||
|
"defaultConfig": "{\"timewindow\":{\"realtime\":{\"interval\":1000,\"timewindowMs\":86400000},\"aggregation\":{\"type\":\"NONE\",\"limit\":200}},\"showTitle\":true,\"showTitleIcon\":true,\"titleIcon\":\"router\",\"backgroundColor\":\"rgb(255, 255, 255)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"4px\",\"settings\":{},\"title\":\"Edge Quick Overview\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px 5px 10px\"},\"useDashboardTimewindow\":false,\"showLegend\":false,\"datasources\":[{\"type\":\"function\",\"name\":\"Simulated\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Sin\",\"color\":\"#2196f3\",\"settings\":{\"columnWidth\":\"0px\",\"useCellStyleFunction\":false,\"cellStyleFunction\":\"\",\"useCellContentFunction\":false,\"cellContentFunction\":\"\"},\"_hash\":0.472295003170325,\"funcBody\":\"return Math.round(1000*Math.sin(time/5000));\"}]}],\"widgetStyle\":{},\"actions\":{}}" |
||||
|
} |
||||
|
} |
||||
|
] |
||||
|
} |
||||
@ -0,0 +1,162 @@ |
|||||
|
{ |
||||
|
"ruleChain": { |
||||
|
"additionalInfo": null, |
||||
|
"name": "Edge Root Rule Chain", |
||||
|
"type": "EDGE", |
||||
|
"firstRuleNodeId": null, |
||||
|
"root": true, |
||||
|
"debugMode": false, |
||||
|
"configuration": null |
||||
|
}, |
||||
|
"metadata": { |
||||
|
"firstNodeIndex": 0, |
||||
|
"nodes": [ |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.", |
||||
|
"layoutX": 203, |
||||
|
"layoutY": 259 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", |
||||
|
"name": "Device Profile Node", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"persistAlarmRulesState": false, |
||||
|
"fetchAlarmRulesStateOnStart": false |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 823, |
||||
|
"layoutY": 157 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", |
||||
|
"name": "Save Timeseries", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"defaultTTL": 0 |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 824, |
||||
|
"layoutY": 52 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", |
||||
|
"name": "Save Client Attributes", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"scope": "CLIENT_SCOPE" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 347, |
||||
|
"layoutY": 149 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", |
||||
|
"name": "Message Type Switch", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"version": 0 |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 825, |
||||
|
"layoutY": 266 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.action.TbLogNode", |
||||
|
"name": "Log RPC from Device", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 824, |
||||
|
"layoutY": 378 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.action.TbLogNode", |
||||
|
"name": "Log Other", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 824, |
||||
|
"layoutY": 466 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", |
||||
|
"name": "RPC Call Request", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"timeoutInSeconds": 60 |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 1134, |
||||
|
"layoutY": 132 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.edge.TbMsgPushToCloudNode", |
||||
|
"name": "Push to cloud", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"version": 0 |
||||
|
} |
||||
|
} |
||||
|
], |
||||
|
"connections": [ |
||||
|
{ |
||||
|
"fromIndex": 0, |
||||
|
"toIndex": 3, |
||||
|
"type": "Success" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 1, |
||||
|
"toIndex": 7, |
||||
|
"type": "Success" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 2, |
||||
|
"toIndex": 7, |
||||
|
"type": "Success" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 3, |
||||
|
"toIndex": 6, |
||||
|
"type": "RPC Request to Device" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 3, |
||||
|
"toIndex": 5, |
||||
|
"type": "Other" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 3, |
||||
|
"toIndex": 2, |
||||
|
"type": "Post attributes" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 3, |
||||
|
"toIndex": 1, |
||||
|
"type": "Post telemetry" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 3, |
||||
|
"toIndex": 4, |
||||
|
"type": "RPC Request from Device" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 4, |
||||
|
"toIndex": 7, |
||||
|
"type": "Success" |
||||
|
} |
||||
|
], |
||||
|
"ruleChainConnections": null |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,87 @@ |
|||||
|
-- |
||||
|
-- Copyright © 2016-2021 The Thingsboard Authors |
||||
|
-- |
||||
|
-- Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
-- you may not use this file except in compliance with the License. |
||||
|
-- You may obtain a copy of the License at |
||||
|
-- |
||||
|
-- http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
-- |
||||
|
-- Unless required by applicable law or agreed to in writing, software |
||||
|
-- distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
-- See the License for the specific language governing permissions and |
||||
|
-- limitations under the License. |
||||
|
-- |
||||
|
|
||||
|
CREATE OR REPLACE PROCEDURE cleanup_timeseries_by_ttl(IN null_uuid uuid, |
||||
|
IN system_ttl bigint, INOUT deleted bigint) |
||||
|
LANGUAGE plpgsql AS |
||||
|
$$ |
||||
|
DECLARE |
||||
|
tenant_cursor CURSOR FOR select tenant.id as tenant_id |
||||
|
from tenant; |
||||
|
tenant_id_record uuid; |
||||
|
customer_id_record uuid; |
||||
|
tenant_ttl bigint; |
||||
|
customer_ttl bigint; |
||||
|
deleted_for_entities bigint; |
||||
|
tenant_ttl_ts bigint; |
||||
|
customer_ttl_ts bigint; |
||||
|
BEGIN |
||||
|
OPEN tenant_cursor; |
||||
|
FETCH tenant_cursor INTO tenant_id_record; |
||||
|
WHILE FOUND |
||||
|
LOOP |
||||
|
EXECUTE format( |
||||
|
'select attribute_kv.long_v from attribute_kv where attribute_kv.entity_id = %L and attribute_kv.attribute_key = %L', |
||||
|
tenant_id_record, 'TTL') INTO tenant_ttl; |
||||
|
if tenant_ttl IS NULL THEN |
||||
|
tenant_ttl := system_ttl; |
||||
|
END IF; |
||||
|
IF tenant_ttl > 0 THEN |
||||
|
tenant_ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - tenant_ttl::bigint * 1000)::bigint; |
||||
|
deleted_for_entities := delete_device_records_from_ts_kv(tenant_id_record, null_uuid, tenant_ttl_ts); |
||||
|
deleted := deleted + deleted_for_entities; |
||||
|
RAISE NOTICE '% telemetry removed for devices where tenant_id = %', deleted_for_entities, tenant_id_record; |
||||
|
deleted_for_entities := delete_asset_records_from_ts_kv(tenant_id_record, null_uuid, tenant_ttl_ts); |
||||
|
deleted := deleted + deleted_for_entities; |
||||
|
RAISE NOTICE '% telemetry removed for assets where tenant_id = %', deleted_for_entities, tenant_id_record; |
||||
|
END IF; |
||||
|
FOR customer_id_record IN |
||||
|
SELECT customer.id AS customer_id FROM customer WHERE customer.tenant_id = tenant_id_record |
||||
|
LOOP |
||||
|
EXECUTE format( |
||||
|
'select attribute_kv.long_v from attribute_kv where attribute_kv.entity_id = %L and attribute_kv.attribute_key = %L', |
||||
|
customer_id_record, 'TTL') INTO customer_ttl; |
||||
|
IF customer_ttl IS NULL THEN |
||||
|
customer_ttl_ts := tenant_ttl_ts; |
||||
|
ELSE |
||||
|
IF customer_ttl > 0 THEN |
||||
|
customer_ttl_ts := |
||||
|
(EXTRACT(EPOCH FROM current_timestamp) * 1000 - |
||||
|
customer_ttl::bigint * 1000)::bigint; |
||||
|
END IF; |
||||
|
END IF; |
||||
|
IF customer_ttl_ts IS NOT NULL AND customer_ttl_ts > 0 THEN |
||||
|
deleted_for_entities := |
||||
|
delete_customer_records_from_ts_kv(tenant_id_record, customer_id_record, |
||||
|
customer_ttl_ts); |
||||
|
deleted := deleted + deleted_for_entities; |
||||
|
RAISE NOTICE '% telemetry removed for customer with id = % where tenant_id = %', deleted_for_entities, customer_id_record, tenant_id_record; |
||||
|
deleted_for_entities := |
||||
|
delete_device_records_from_ts_kv(tenant_id_record, customer_id_record, |
||||
|
customer_ttl_ts); |
||||
|
deleted := deleted + deleted_for_entities; |
||||
|
RAISE NOTICE '% telemetry removed for devices where tenant_id = % and customer_id = %', deleted_for_entities, tenant_id_record, customer_id_record; |
||||
|
deleted_for_entities := delete_asset_records_from_ts_kv(tenant_id_record, |
||||
|
customer_id_record, |
||||
|
customer_ttl_ts); |
||||
|
deleted := deleted + deleted_for_entities; |
||||
|
RAISE NOTICE '% telemetry removed for assets where tenant_id = % and customer_id = %', deleted_for_entities, tenant_id_record, customer_id_record; |
||||
|
END IF; |
||||
|
END LOOP; |
||||
|
FETCH tenant_cursor INTO tenant_id_record; |
||||
|
END LOOP; |
||||
|
END |
||||
|
$$; |
||||
@ -0,0 +1,47 @@ |
|||||
|
-- |
||||
|
-- Copyright © 2016-2021 The Thingsboard Authors |
||||
|
-- |
||||
|
-- Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
-- you may not use this file except in compliance with the License. |
||||
|
-- You may obtain a copy of the License at |
||||
|
-- |
||||
|
-- http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
-- |
||||
|
-- Unless required by applicable law or agreed to in writing, software |
||||
|
-- distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
-- See the License for the specific language governing permissions and |
||||
|
-- limitations under the License. |
||||
|
-- |
||||
|
|
||||
|
CREATE TABLE IF NOT EXISTS edge ( |
||||
|
id uuid NOT NULL CONSTRAINT edge_pkey PRIMARY KEY, |
||||
|
created_time bigint NOT NULL, |
||||
|
additional_info varchar, |
||||
|
customer_id uuid, |
||||
|
root_rule_chain_id uuid, |
||||
|
type varchar(255), |
||||
|
name varchar(255), |
||||
|
label varchar(255), |
||||
|
routing_key varchar(255), |
||||
|
secret varchar(255), |
||||
|
edge_license_key varchar(30), |
||||
|
cloud_endpoint varchar(255), |
||||
|
search_text varchar(255), |
||||
|
tenant_id uuid, |
||||
|
CONSTRAINT edge_name_unq_key UNIQUE (tenant_id, name), |
||||
|
CONSTRAINT edge_routing_key_unq_key UNIQUE (routing_key) |
||||
|
); |
||||
|
|
||||
|
CREATE TABLE IF NOT EXISTS edge_event ( |
||||
|
id uuid NOT NULL CONSTRAINT edge_event_pkey PRIMARY KEY, |
||||
|
created_time bigint NOT NULL, |
||||
|
edge_id uuid, |
||||
|
edge_event_type varchar(255), |
||||
|
edge_event_uid varchar(255), |
||||
|
entity_id uuid, |
||||
|
edge_event_action varchar(255), |
||||
|
body varchar(10000000), |
||||
|
tenant_id uuid, |
||||
|
ts bigint NOT NULL |
||||
|
); |
||||
@ -0,0 +1,32 @@ |
|||||
|
-- |
||||
|
-- Copyright © 2016-2021 The Thingsboard Authors |
||||
|
-- |
||||
|
-- Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
-- you may not use this file except in compliance with the License. |
||||
|
-- You may obtain a copy of the License at |
||||
|
-- |
||||
|
-- http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
-- |
||||
|
-- Unless required by applicable law or agreed to in writing, software |
||||
|
-- distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
-- See the License for the specific language governing permissions and |
||||
|
-- limitations under the License. |
||||
|
-- |
||||
|
|
||||
|
CREATE OR REPLACE PROCEDURE cleanup_edge_events_by_ttl(IN ttl bigint, INOUT deleted bigint) |
||||
|
LANGUAGE plpgsql AS |
||||
|
$$ |
||||
|
DECLARE |
||||
|
ttl_ts bigint; |
||||
|
ttl_deleted_count bigint DEFAULT 0; |
||||
|
BEGIN |
||||
|
IF ttl > 0 THEN |
||||
|
ttl_ts := (EXTRACT(EPOCH FROM current_timestamp) * 1000 - ttl::bigint * 1000)::bigint; |
||||
|
EXECUTE format( |
||||
|
'WITH deleted AS (DELETE FROM edge_event WHERE ts < %L::bigint RETURNING *) SELECT count(*) FROM deleted', ttl_ts) into ttl_deleted_count; |
||||
|
END IF; |
||||
|
RAISE NOTICE 'Edge events removed by ttl: %', ttl_deleted_count; |
||||
|
deleted := ttl_deleted_count; |
||||
|
END |
||||
|
$$; |
||||
@ -0,0 +1,593 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.controller; |
||||
|
|
||||
|
import com.google.common.util.concurrent.ListenableFuture; |
||||
|
import org.springframework.http.HttpStatus; |
||||
|
import org.springframework.security.access.prepost.PreAuthorize; |
||||
|
import org.springframework.web.bind.annotation.PathVariable; |
||||
|
import org.springframework.web.bind.annotation.RequestBody; |
||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestMethod; |
||||
|
import org.springframework.web.bind.annotation.RequestParam; |
||||
|
import org.springframework.web.bind.annotation.ResponseBody; |
||||
|
import org.springframework.web.bind.annotation.ResponseStatus; |
||||
|
import org.springframework.web.bind.annotation.RestController; |
||||
|
import org.thingsboard.server.common.data.Customer; |
||||
|
import org.thingsboard.server.common.data.EntitySubtype; |
||||
|
import org.thingsboard.server.common.data.EntityType; |
||||
|
import org.thingsboard.server.common.data.audit.ActionType; |
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeInfo; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeSearchQuery; |
||||
|
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
||||
|
import org.thingsboard.server.common.data.exception.ThingsboardException; |
||||
|
import org.thingsboard.server.common.data.id.CustomerId; |
||||
|
import org.thingsboard.server.common.data.id.EdgeId; |
||||
|
import org.thingsboard.server.common.data.id.RuleChainId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
||||
|
import org.thingsboard.server.common.data.rule.RuleChain; |
||||
|
import org.thingsboard.server.common.data.security.Authority; |
||||
|
import org.thingsboard.server.dao.exception.DataValidationException; |
||||
|
import org.thingsboard.server.dao.exception.IncorrectParameterException; |
||||
|
import org.thingsboard.server.dao.model.ModelConstants; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.edge.rpc.EdgeGrpcSession; |
||||
|
import org.thingsboard.server.service.security.model.SecurityUser; |
||||
|
import org.thingsboard.server.service.security.permission.Operation; |
||||
|
import org.thingsboard.server.service.security.permission.Resource; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
@RestController |
||||
|
@TbCoreComponent |
||||
|
@RequestMapping("/api") |
||||
|
public class EdgeController extends BaseController { |
||||
|
|
||||
|
public static final String EDGE_ID = "edgeId"; |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") |
||||
|
@RequestMapping(value = "/edges/enabled", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public boolean isEdgesSupportEnabled() { |
||||
|
return edgesEnabled; |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
||||
|
@RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public Edge getEdgeById(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { |
||||
|
checkParameter(EDGE_ID, strEdgeId); |
||||
|
try { |
||||
|
EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); |
||||
|
Edge edge = checkEdgeId(edgeId, Operation.READ); |
||||
|
if (Authority.CUSTOMER_USER.equals(getCurrentUser().getAuthority())) { |
||||
|
cleanUpSensitiveData(edge); |
||||
|
} |
||||
|
return edge; |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
||||
|
@RequestMapping(value = "/edge/info/{edgeId}", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public EdgeInfo getEdgeInfoById(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { |
||||
|
checkParameter(EDGE_ID, strEdgeId); |
||||
|
try { |
||||
|
EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); |
||||
|
EdgeInfo edgeInfo = checkEdgeInfoId(edgeId, Operation.READ); |
||||
|
if (Authority.CUSTOMER_USER.equals(getCurrentUser().getAuthority())) { |
||||
|
cleanUpSensitiveData(edgeInfo); |
||||
|
} |
||||
|
return edgeInfo; |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/edge", method = RequestMethod.POST) |
||||
|
@ResponseBody |
||||
|
public Edge saveEdge(@RequestBody Edge edge) throws ThingsboardException { |
||||
|
try { |
||||
|
TenantId tenantId = getCurrentUser().getTenantId(); |
||||
|
edge.setTenantId(tenantId); |
||||
|
boolean created = edge.getId() == null; |
||||
|
|
||||
|
RuleChain edgeTemplateRootRuleChain = null; |
||||
|
if (created) { |
||||
|
edgeTemplateRootRuleChain = ruleChainService.getEdgeTemplateRootRuleChain(tenantId); |
||||
|
if (edgeTemplateRootRuleChain == null) { |
||||
|
throw new DataValidationException("Root edge rule chain is not available!"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
Operation operation = created ? Operation.CREATE : Operation.WRITE; |
||||
|
|
||||
|
accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, operation, |
||||
|
edge.getId(), edge); |
||||
|
|
||||
|
Edge savedEdge = checkNotNull(edgeService.saveEdge(edge)); |
||||
|
|
||||
|
if (created) { |
||||
|
ruleChainService.assignRuleChainToEdge(tenantId, edgeTemplateRootRuleChain.getId(), savedEdge.getId()); |
||||
|
edgeNotificationService.setEdgeRootRuleChain(tenantId, savedEdge, edgeTemplateRootRuleChain.getId()); |
||||
|
edgeService.assignDefaultRuleChainsToEdge(tenantId, savedEdge.getId()); |
||||
|
} |
||||
|
|
||||
|
tbClusterService.onEntityStateChange(savedEdge.getTenantId(), savedEdge.getId(), |
||||
|
created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); |
||||
|
|
||||
|
logEntityAction(savedEdge.getId(), savedEdge, null, created ? ActionType.ADDED : ActionType.UPDATED, null); |
||||
|
return savedEdge; |
||||
|
} catch (Exception e) { |
||||
|
logEntityAction(emptyId(EntityType.EDGE), edge, |
||||
|
null, edge.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e); |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/edge/{edgeId}", method = RequestMethod.DELETE) |
||||
|
@ResponseStatus(value = HttpStatus.OK) |
||||
|
public void deleteEdge(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { |
||||
|
checkParameter(EDGE_ID, strEdgeId); |
||||
|
try { |
||||
|
EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); |
||||
|
Edge edge = checkEdgeId(edgeId, Operation.DELETE); |
||||
|
edgeService.deleteEdge(getTenantId(), edgeId); |
||||
|
|
||||
|
tbClusterService.onEntityStateChange(getTenantId(), edgeId, |
||||
|
ComponentLifecycleEvent.DELETED); |
||||
|
|
||||
|
logEntityAction(edgeId, edge, |
||||
|
null, |
||||
|
ActionType.DELETED, null, strEdgeId); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
|
||||
|
logEntityAction(emptyId(EntityType.EDGE), |
||||
|
null, |
||||
|
null, |
||||
|
ActionType.DELETED, e, strEdgeId); |
||||
|
|
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/edges", params = {"pageSize", "page"}, method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public PageData<Edge> getEdges(@RequestParam int pageSize, |
||||
|
@RequestParam int page, |
||||
|
@RequestParam(required = false) String textSearch, |
||||
|
@RequestParam(required = false) String sortProperty, |
||||
|
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
||||
|
try { |
||||
|
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
||||
|
TenantId tenantId = getCurrentUser().getTenantId(); |
||||
|
return checkNotNull(edgeService.findEdgesByTenantId(tenantId, pageLink)); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/customer/{customerId}/edge/{edgeId}", method = RequestMethod.POST) |
||||
|
@ResponseBody |
||||
|
public Edge assignEdgeToCustomer(@PathVariable("customerId") String strCustomerId, |
||||
|
@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { |
||||
|
checkParameter("customerId", strCustomerId); |
||||
|
checkParameter(EDGE_ID, strEdgeId); |
||||
|
try { |
||||
|
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); |
||||
|
Customer customer = checkCustomerId(customerId, Operation.READ); |
||||
|
|
||||
|
EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); |
||||
|
checkEdgeId(edgeId, Operation.ASSIGN_TO_CUSTOMER); |
||||
|
|
||||
|
Edge savedEdge = checkNotNull(edgeService.assignEdgeToCustomer(getCurrentUser().getTenantId(), edgeId, customerId)); |
||||
|
|
||||
|
tbClusterService.onEntityStateChange(getTenantId(), edgeId, |
||||
|
ComponentLifecycleEvent.UPDATED); |
||||
|
|
||||
|
logEntityAction(edgeId, savedEdge, |
||||
|
savedEdge.getCustomerId(), |
||||
|
ActionType.ASSIGNED_TO_CUSTOMER, null, strEdgeId, strCustomerId, customer.getName()); |
||||
|
|
||||
|
sendEntityAssignToCustomerNotificationMsg(savedEdge.getTenantId(), savedEdge.getId(), |
||||
|
customerId, EdgeEventActionType.ASSIGNED_TO_CUSTOMER); |
||||
|
|
||||
|
return savedEdge; |
||||
|
} catch (Exception e) { |
||||
|
logEntityAction(emptyId(EntityType.EDGE), null, |
||||
|
null, |
||||
|
ActionType.ASSIGNED_TO_CUSTOMER, e, strEdgeId, strCustomerId); |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/customer/edge/{edgeId}", method = RequestMethod.DELETE) |
||||
|
@ResponseBody |
||||
|
public Edge unassignEdgeFromCustomer(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { |
||||
|
checkParameter(EDGE_ID, strEdgeId); |
||||
|
try { |
||||
|
EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); |
||||
|
Edge edge = checkEdgeId(edgeId, Operation.UNASSIGN_FROM_CUSTOMER); |
||||
|
if (edge.getCustomerId() == null || edge.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { |
||||
|
throw new IncorrectParameterException("Edge isn't assigned to any customer!"); |
||||
|
} |
||||
|
Customer customer = checkCustomerId(edge.getCustomerId(), Operation.READ); |
||||
|
|
||||
|
Edge savedEdge = checkNotNull(edgeService.unassignEdgeFromCustomer(getCurrentUser().getTenantId(), edgeId)); |
||||
|
|
||||
|
tbClusterService.onEntityStateChange(getTenantId(), edgeId, |
||||
|
ComponentLifecycleEvent.UPDATED); |
||||
|
|
||||
|
logEntityAction(edgeId, edge, |
||||
|
edge.getCustomerId(), |
||||
|
ActionType.UNASSIGNED_FROM_CUSTOMER, null, strEdgeId, customer.getId().toString(), customer.getName()); |
||||
|
|
||||
|
sendEntityAssignToCustomerNotificationMsg(savedEdge.getTenantId(), savedEdge.getId(), |
||||
|
customer.getId(), EdgeEventActionType.UNASSIGNED_FROM_CUSTOMER); |
||||
|
|
||||
|
return savedEdge; |
||||
|
} catch (Exception e) { |
||||
|
logEntityAction(emptyId(EntityType.EDGE), null, |
||||
|
null, |
||||
|
ActionType.UNASSIGNED_FROM_CUSTOMER, e, strEdgeId); |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/customer/public/edge/{edgeId}", method = RequestMethod.POST) |
||||
|
@ResponseBody |
||||
|
public Edge assignEdgeToPublicCustomer(@PathVariable(EDGE_ID) String strEdgeId) throws ThingsboardException { |
||||
|
checkParameter(EDGE_ID, strEdgeId); |
||||
|
try { |
||||
|
EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); |
||||
|
Edge edge = checkEdgeId(edgeId, Operation.ASSIGN_TO_CUSTOMER); |
||||
|
Customer publicCustomer = customerService.findOrCreatePublicCustomer(edge.getTenantId()); |
||||
|
Edge savedEdge = checkNotNull(edgeService.assignEdgeToCustomer(getCurrentUser().getTenantId(), edgeId, publicCustomer.getId())); |
||||
|
|
||||
|
tbClusterService.onEntityStateChange(getTenantId(), edgeId, |
||||
|
ComponentLifecycleEvent.UPDATED); |
||||
|
|
||||
|
logEntityAction(edgeId, savedEdge, |
||||
|
savedEdge.getCustomerId(), |
||||
|
ActionType.ASSIGNED_TO_CUSTOMER, null, strEdgeId, publicCustomer.getId().toString(), publicCustomer.getName()); |
||||
|
|
||||
|
return savedEdge; |
||||
|
} catch (Exception e) { |
||||
|
logEntityAction(emptyId(EntityType.EDGE), null, |
||||
|
null, |
||||
|
ActionType.ASSIGNED_TO_CUSTOMER, e, strEdgeId); |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/tenant/edges", params = {"pageSize", "page"}, method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public PageData<Edge> getTenantEdges( |
||||
|
@RequestParam int pageSize, |
||||
|
@RequestParam int page, |
||||
|
@RequestParam(required = false) String type, |
||||
|
@RequestParam(required = false) String textSearch, |
||||
|
@RequestParam(required = false) String sortProperty, |
||||
|
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
||||
|
try { |
||||
|
TenantId tenantId = getCurrentUser().getTenantId(); |
||||
|
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
||||
|
if (type != null && type.trim().length() > 0) { |
||||
|
return checkNotNull(edgeService.findEdgesByTenantIdAndType(tenantId, type, pageLink)); |
||||
|
} else { |
||||
|
return checkNotNull(edgeService.findEdgesByTenantId(tenantId, pageLink)); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/tenant/edgeInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public PageData<EdgeInfo> getTenantEdgeInfos( |
||||
|
@RequestParam int pageSize, |
||||
|
@RequestParam int page, |
||||
|
@RequestParam(required = false) String type, |
||||
|
@RequestParam(required = false) String textSearch, |
||||
|
@RequestParam(required = false) String sortProperty, |
||||
|
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
||||
|
try { |
||||
|
TenantId tenantId = getCurrentUser().getTenantId(); |
||||
|
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
||||
|
if (type != null && type.trim().length() > 0) { |
||||
|
return checkNotNull(edgeService.findEdgeInfosByTenantIdAndType(tenantId, type, pageLink)); |
||||
|
} else { |
||||
|
return checkNotNull(edgeService.findEdgeInfosByTenantId(tenantId, pageLink)); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/tenant/edges", params = {"edgeName"}, method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public Edge getTenantEdge(@RequestParam String edgeName) throws ThingsboardException { |
||||
|
try { |
||||
|
TenantId tenantId = getCurrentUser().getTenantId(); |
||||
|
return checkNotNull(edgeService.findEdgeByTenantIdAndName(tenantId, edgeName)); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/edge/{edgeId}/{ruleChainId}/root", method = RequestMethod.POST) |
||||
|
@ResponseBody |
||||
|
public Edge setRootRuleChain(@PathVariable(EDGE_ID) String strEdgeId, |
||||
|
@PathVariable("ruleChainId") String strRuleChainId) throws ThingsboardException { |
||||
|
checkParameter(EDGE_ID, strEdgeId); |
||||
|
checkParameter("ruleChainId", strRuleChainId); |
||||
|
try { |
||||
|
RuleChainId ruleChainId = new RuleChainId(toUUID(strRuleChainId)); |
||||
|
checkRuleChain(ruleChainId, Operation.WRITE); |
||||
|
|
||||
|
EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); |
||||
|
Edge edge = checkEdgeId(edgeId, Operation.WRITE); |
||||
|
accessControlService.checkPermission(getCurrentUser(), Resource.EDGE, Operation.WRITE, |
||||
|
edge.getId(), edge); |
||||
|
|
||||
|
Edge updatedEdge = edgeNotificationService.setEdgeRootRuleChain(getTenantId(), edge, ruleChainId); |
||||
|
|
||||
|
tbClusterService.onEntityStateChange(updatedEdge.getTenantId(), updatedEdge.getId(), ComponentLifecycleEvent.UPDATED); |
||||
|
|
||||
|
logEntityAction(updatedEdge.getId(), updatedEdge, null, ActionType.UPDATED, null); |
||||
|
|
||||
|
return updatedEdge; |
||||
|
} catch (Exception e) { |
||||
|
logEntityAction(emptyId(EntityType.EDGE), |
||||
|
null, |
||||
|
null, |
||||
|
ActionType.UPDATED, e, strEdgeId); |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
||||
|
@RequestMapping(value = "/customer/{customerId}/edges", params = {"pageSize", "page"}, method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public PageData<Edge> getCustomerEdges( |
||||
|
@PathVariable("customerId") String strCustomerId, |
||||
|
@RequestParam int pageSize, |
||||
|
@RequestParam int page, |
||||
|
@RequestParam(required = false) String type, |
||||
|
@RequestParam(required = false) String textSearch, |
||||
|
@RequestParam(required = false) String sortProperty, |
||||
|
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
||||
|
checkParameter("customerId", strCustomerId); |
||||
|
try { |
||||
|
SecurityUser user = getCurrentUser(); |
||||
|
TenantId tenantId = user.getTenantId(); |
||||
|
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); |
||||
|
checkCustomerId(customerId, Operation.READ); |
||||
|
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
||||
|
PageData<Edge> result; |
||||
|
if (type != null && type.trim().length() > 0) { |
||||
|
result = edgeService.findEdgesByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink); |
||||
|
} else { |
||||
|
result = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink); |
||||
|
} |
||||
|
if (Authority.CUSTOMER_USER.equals(user.getAuthority())) { |
||||
|
for (Edge edge : result.getData()) { |
||||
|
cleanUpSensitiveData(edge); |
||||
|
} |
||||
|
} |
||||
|
return checkNotNull(result); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
||||
|
@RequestMapping(value = "/customer/{customerId}/edgeInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public PageData<EdgeInfo> getCustomerEdgeInfos( |
||||
|
@PathVariable("customerId") String strCustomerId, |
||||
|
@RequestParam int pageSize, |
||||
|
@RequestParam int page, |
||||
|
@RequestParam(required = false) String type, |
||||
|
@RequestParam(required = false) String textSearch, |
||||
|
@RequestParam(required = false) String sortProperty, |
||||
|
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
||||
|
checkParameter("customerId", strCustomerId); |
||||
|
try { |
||||
|
SecurityUser user = getCurrentUser(); |
||||
|
TenantId tenantId = user.getTenantId(); |
||||
|
CustomerId customerId = new CustomerId(toUUID(strCustomerId)); |
||||
|
checkCustomerId(customerId, Operation.READ); |
||||
|
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
||||
|
PageData<EdgeInfo> result; |
||||
|
if (type != null && type.trim().length() > 0) { |
||||
|
result = edgeService.findEdgeInfosByTenantIdAndCustomerIdAndType(tenantId, customerId, type, pageLink); |
||||
|
} else { |
||||
|
result = edgeService.findEdgeInfosByTenantIdAndCustomerId(tenantId, customerId, pageLink); |
||||
|
} |
||||
|
if (Authority.CUSTOMER_USER.equals(user.getAuthority())) { |
||||
|
for (Edge edge : result.getData()) { |
||||
|
cleanUpSensitiveData(edge); |
||||
|
} |
||||
|
} |
||||
|
return checkNotNull(result); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
||||
|
@RequestMapping(value = "/edges", params = {"edgeIds"}, method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public List<Edge> getEdgesByIds( |
||||
|
@RequestParam("edgeIds") String[] strEdgeIds) throws ThingsboardException { |
||||
|
checkArrayParameter("edgeIds", strEdgeIds); |
||||
|
try { |
||||
|
SecurityUser user = getCurrentUser(); |
||||
|
TenantId tenantId = user.getTenantId(); |
||||
|
CustomerId customerId = user.getCustomerId(); |
||||
|
List<EdgeId> edgeIds = new ArrayList<>(); |
||||
|
for (String strEdgeId : strEdgeIds) { |
||||
|
edgeIds.add(new EdgeId(toUUID(strEdgeId))); |
||||
|
} |
||||
|
ListenableFuture<List<Edge>> edgesFuture; |
||||
|
if (customerId == null || customerId.isNullUid()) { |
||||
|
edgesFuture = edgeService.findEdgesByTenantIdAndIdsAsync(tenantId, edgeIds); |
||||
|
} else { |
||||
|
edgesFuture = edgeService.findEdgesByTenantIdCustomerIdAndIdsAsync(tenantId, customerId, edgeIds); |
||||
|
} |
||||
|
List<Edge> edges = edgesFuture.get(); |
||||
|
if (Authority.CUSTOMER_USER.equals(user.getAuthority())) { |
||||
|
for (Edge edge : edges) { |
||||
|
cleanUpSensitiveData(edge); |
||||
|
} |
||||
|
} |
||||
|
return checkNotNull(edges); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
||||
|
@RequestMapping(value = "/edges", method = RequestMethod.POST) |
||||
|
@ResponseBody |
||||
|
public List<Edge> findByQuery(@RequestBody EdgeSearchQuery query) throws ThingsboardException { |
||||
|
checkNotNull(query); |
||||
|
checkNotNull(query.getParameters()); |
||||
|
checkNotNull(query.getEdgeTypes()); |
||||
|
checkEntityId(query.getParameters().getEntityId(), Operation.READ); |
||||
|
try { |
||||
|
SecurityUser user = getCurrentUser(); |
||||
|
TenantId tenantId = user.getTenantId(); |
||||
|
List<Edge> edges = checkNotNull(edgeService.findEdgesByQuery(tenantId, query).get()); |
||||
|
edges = edges.stream().filter(edge -> { |
||||
|
try { |
||||
|
accessControlService.checkPermission(user, Resource.EDGE, Operation.READ, edge.getId(), edge); |
||||
|
return true; |
||||
|
} catch (ThingsboardException e) { |
||||
|
return false; |
||||
|
} |
||||
|
}).collect(Collectors.toList()); |
||||
|
if (Authority.CUSTOMER_USER.equals(user.getAuthority())) { |
||||
|
for (Edge edge : edges) { |
||||
|
cleanUpSensitiveData(edge); |
||||
|
} |
||||
|
} |
||||
|
return edges; |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
||||
|
@RequestMapping(value = "/edge/types", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public List<EntitySubtype> getEdgeTypes() throws ThingsboardException { |
||||
|
try { |
||||
|
SecurityUser user = getCurrentUser(); |
||||
|
TenantId tenantId = user.getTenantId(); |
||||
|
ListenableFuture<List<EntitySubtype>> edgeTypes = edgeService.findEdgeTypesByTenantId(tenantId); |
||||
|
return checkNotNull(edgeTypes.get()); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/edge/sync/{edgeId}", method = RequestMethod.POST) |
||||
|
public void syncEdge(@PathVariable("edgeId") String strEdgeId) throws ThingsboardException { |
||||
|
checkParameter("edgeId", strEdgeId); |
||||
|
try { |
||||
|
if (isEdgesEnabled()) { |
||||
|
EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); |
||||
|
edgeId = checkNotNull(edgeId); |
||||
|
SecurityUser user = getCurrentUser(); |
||||
|
TenantId tenantId = user.getTenantId(); |
||||
|
EdgeGrpcSession session = edgeGrpcService.getEdgeGrpcSessionById(tenantId, edgeId); |
||||
|
Edge edge = session.getEdge(); |
||||
|
syncEdgeService.sync(tenantId, edge); |
||||
|
} else { |
||||
|
throw new ThingsboardException("Edges support disabled", ThingsboardErrorCode.GENERAL); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@RequestMapping(value = "/license/checkInstance", method = RequestMethod.POST) |
||||
|
@ResponseBody |
||||
|
public Object checkInstance(@RequestBody Object request) throws ThingsboardException { |
||||
|
try { |
||||
|
return edgeService.checkInstance(request); |
||||
|
} catch (Exception e) { |
||||
|
throw new ThingsboardException(e, ThingsboardErrorCode.SUBSCRIPTION_VIOLATION); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@RequestMapping(value = "/license/activateInstance", params = {"licenseSecret", "releaseDate"}, method = RequestMethod.POST) |
||||
|
@ResponseBody |
||||
|
public Object activateInstance(@RequestParam String licenseSecret, |
||||
|
@RequestParam String releaseDate) throws ThingsboardException { |
||||
|
try { |
||||
|
return edgeService.activateInstance(licenseSecret, releaseDate); |
||||
|
} catch (Exception e) { |
||||
|
throw new ThingsboardException(e, ThingsboardErrorCode.SUBSCRIPTION_VIOLATION); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/edge/missingToRelatedRuleChains/{edgeId}", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public String findMissingToRelatedRuleChains(@PathVariable("edgeId") String strEdgeId) throws ThingsboardException { |
||||
|
try { |
||||
|
EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); |
||||
|
edgeId = checkNotNull(edgeId); |
||||
|
SecurityUser user = getCurrentUser(); |
||||
|
TenantId tenantId = user.getTenantId(); |
||||
|
return edgeService.findMissingToRelatedRuleChains(tenantId, edgeId); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void cleanUpSensitiveData(Edge edge) { |
||||
|
edge.setEdgeLicenseKey(null); |
||||
|
edge.setRoutingKey(null); |
||||
|
edge.setSecret(null); |
||||
|
edge.setCloudEndpoint(null); |
||||
|
edge.setRootRuleChainId(null); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,71 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.controller; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.security.access.prepost.PreAuthorize; |
||||
|
import org.springframework.web.bind.annotation.PathVariable; |
||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestMethod; |
||||
|
import org.springframework.web.bind.annotation.RequestParam; |
||||
|
import org.springframework.web.bind.annotation.ResponseBody; |
||||
|
import org.springframework.web.bind.annotation.RestController; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEvent; |
||||
|
import org.thingsboard.server.common.data.exception.ThingsboardException; |
||||
|
import org.thingsboard.server.common.data.id.EdgeId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.TimePageLink; |
||||
|
import org.thingsboard.server.dao.edge.EdgeEventService; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.security.permission.Operation; |
||||
|
|
||||
|
@Slf4j |
||||
|
@RestController |
||||
|
@TbCoreComponent |
||||
|
@RequestMapping("/api") |
||||
|
public class EdgeEventController extends BaseController { |
||||
|
|
||||
|
@Autowired |
||||
|
private EdgeEventService edgeEventService; |
||||
|
|
||||
|
public static final String EDGE_ID = "edgeId"; |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/edge/{edgeId}/events", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public PageData<EdgeEvent> getEdgeEvents( |
||||
|
@PathVariable(EDGE_ID) String strEdgeId, |
||||
|
@RequestParam int pageSize, |
||||
|
@RequestParam int page, |
||||
|
@RequestParam(required = false) String textSearch, |
||||
|
@RequestParam(required = false) String sortProperty, |
||||
|
@RequestParam(required = false) String sortOrder, |
||||
|
@RequestParam(required = false) Long startTime, |
||||
|
@RequestParam(required = false) Long endTime) throws ThingsboardException { |
||||
|
checkParameter(EDGE_ID, strEdgeId); |
||||
|
try { |
||||
|
TenantId tenantId = getCurrentUser().getTenantId(); |
||||
|
EdgeId edgeId = new EdgeId(toUUID(strEdgeId)); |
||||
|
checkEdgeId(edgeId, Operation.READ); |
||||
|
TimePageLink pageLink = createTimePageLink(pageSize, page, textSearch, sortProperty, sortOrder, startTime, endTime); |
||||
|
return checkNotNull(edgeEventService.findEdgeEvents(tenantId, edgeId, pageLink, false)); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,505 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge; |
||||
|
|
||||
|
import com.fasterxml.jackson.core.JsonProcessingException; |
||||
|
import com.fasterxml.jackson.databind.JsonNode; |
||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
|
import com.google.common.util.concurrent.FutureCallback; |
||||
|
import com.google.common.util.concurrent.Futures; |
||||
|
import com.google.common.util.concurrent.ListenableFuture; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.checkerframework.checker.nullness.qual.Nullable; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.server.common.data.EdgeUtils; |
||||
|
import org.thingsboard.server.common.data.EntityType; |
||||
|
import org.thingsboard.server.common.data.User; |
||||
|
import org.thingsboard.server.common.data.alarm.Alarm; |
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEvent; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventType; |
||||
|
import org.thingsboard.server.common.data.id.AlarmId; |
||||
|
import org.thingsboard.server.common.data.id.CustomerId; |
||||
|
import org.thingsboard.server.common.data.id.EdgeId; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
import org.thingsboard.server.common.data.id.EntityIdFactory; |
||||
|
import org.thingsboard.server.common.data.id.RuleChainId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.common.data.page.TimePageLink; |
||||
|
import org.thingsboard.server.common.data.relation.EntityRelation; |
||||
|
import org.thingsboard.server.common.data.rule.RuleChain; |
||||
|
import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; |
||||
|
import org.thingsboard.server.common.msg.queue.TbCallback; |
||||
|
import org.thingsboard.server.dao.alarm.AlarmService; |
||||
|
import org.thingsboard.server.dao.edge.EdgeEventService; |
||||
|
import org.thingsboard.server.dao.edge.EdgeService; |
||||
|
import org.thingsboard.server.dao.rule.RuleChainService; |
||||
|
import org.thingsboard.server.dao.user.UserService; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.executors.DbCallbackExecutorService; |
||||
|
import org.thingsboard.server.service.queue.TbClusterService; |
||||
|
|
||||
|
import javax.annotation.PostConstruct; |
||||
|
import javax.annotation.PreDestroy; |
||||
|
import java.io.IOException; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.HashSet; |
||||
|
import java.util.List; |
||||
|
import java.util.Set; |
||||
|
import java.util.UUID; |
||||
|
import java.util.concurrent.ExecutorService; |
||||
|
import java.util.concurrent.Executors; |
||||
|
|
||||
|
@Service |
||||
|
@TbCoreComponent |
||||
|
@Slf4j |
||||
|
public class DefaultEdgeNotificationService implements EdgeNotificationService { |
||||
|
|
||||
|
private static final ObjectMapper mapper = new ObjectMapper(); |
||||
|
|
||||
|
private static final int DEFAULT_LIMIT = 100; |
||||
|
|
||||
|
@Autowired |
||||
|
private EdgeService edgeService; |
||||
|
|
||||
|
@Autowired |
||||
|
private AlarmService alarmService; |
||||
|
|
||||
|
@Autowired |
||||
|
private UserService userService; |
||||
|
|
||||
|
@Autowired |
||||
|
private RuleChainService ruleChainService; |
||||
|
|
||||
|
@Autowired |
||||
|
private EdgeEventService edgeEventService; |
||||
|
|
||||
|
@Autowired |
||||
|
private TbClusterService clusterService; |
||||
|
|
||||
|
@Autowired |
||||
|
private DbCallbackExecutorService dbCallbackExecutorService; |
||||
|
|
||||
|
private ExecutorService tsCallBackExecutor; |
||||
|
|
||||
|
@PostConstruct |
||||
|
public void initExecutor() { |
||||
|
tsCallBackExecutor = Executors.newSingleThreadExecutor(); |
||||
|
} |
||||
|
|
||||
|
@PreDestroy |
||||
|
public void shutdownExecutor() { |
||||
|
if (tsCallBackExecutor != null) { |
||||
|
tsCallBackExecutor.shutdownNow(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException { |
||||
|
edge.setRootRuleChainId(ruleChainId); |
||||
|
Edge savedEdge = edgeService.saveEdge(edge); |
||||
|
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, EdgeEventActionType.UPDATED, ruleChainId, null); |
||||
|
return savedEdge; |
||||
|
} |
||||
|
|
||||
|
private void saveEdgeEvent(TenantId tenantId, |
||||
|
EdgeId edgeId, |
||||
|
EdgeEventType type, |
||||
|
EdgeEventActionType action, |
||||
|
EntityId entityId, |
||||
|
JsonNode body) { |
||||
|
log.debug("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], type [{}], action[{}], entityId [{}], body [{}]", |
||||
|
tenantId, edgeId, type, action, entityId, body); |
||||
|
|
||||
|
EdgeEvent edgeEvent = new EdgeEvent(); |
||||
|
edgeEvent.setEdgeId(edgeId); |
||||
|
edgeEvent.setTenantId(tenantId); |
||||
|
edgeEvent.setType(type); |
||||
|
edgeEvent.setAction(action); |
||||
|
if (entityId != null) { |
||||
|
edgeEvent.setEntityId(entityId.getId()); |
||||
|
} |
||||
|
edgeEvent.setBody(body); |
||||
|
ListenableFuture<EdgeEvent> future = edgeEventService.saveAsync(edgeEvent); |
||||
|
Futures.addCallback(future, new FutureCallback<EdgeEvent>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable EdgeEvent result) { |
||||
|
clusterService.onEdgeEventUpdate(tenantId, edgeId); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.warn("[{}] Can't save edge event [{}] for edge [{}]", tenantId.getId(), edgeEvent, edgeId.getId(), t); |
||||
|
} |
||||
|
}, dbCallbackExecutorService); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback) { |
||||
|
try { |
||||
|
TenantId tenantId = new TenantId(new UUID(edgeNotificationMsg.getTenantIdMSB(), edgeNotificationMsg.getTenantIdLSB())); |
||||
|
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); |
||||
|
switch (type) { |
||||
|
case EDGE: |
||||
|
processEdge(tenantId, edgeNotificationMsg); |
||||
|
break; |
||||
|
case USER: |
||||
|
case ASSET: |
||||
|
case DEVICE: |
||||
|
case DEVICE_PROFILE: |
||||
|
case ENTITY_VIEW: |
||||
|
case DASHBOARD: |
||||
|
case RULE_CHAIN: |
||||
|
processEntity(tenantId, edgeNotificationMsg); |
||||
|
break; |
||||
|
case CUSTOMER: |
||||
|
processCustomer(tenantId, edgeNotificationMsg); |
||||
|
break; |
||||
|
case WIDGETS_BUNDLE: |
||||
|
case WIDGET_TYPE: |
||||
|
processWidgetBundleOrWidgetType(tenantId, edgeNotificationMsg); |
||||
|
break; |
||||
|
case ALARM: |
||||
|
processAlarm(tenantId, edgeNotificationMsg); |
||||
|
break; |
||||
|
case RELATION: |
||||
|
processRelation(tenantId, edgeNotificationMsg); |
||||
|
break; |
||||
|
default: |
||||
|
log.debug("Edge event type [{}] is not designed to be pushed to edge", type); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
callback.onFailure(e); |
||||
|
log.error("Can't push to edge updates, edgeNotificationMsg [{}]", edgeNotificationMsg, e); |
||||
|
} finally { |
||||
|
callback.onSuccess(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void processEdge(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { |
||||
|
try { |
||||
|
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()); |
||||
|
EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); |
||||
|
ListenableFuture<Edge> edgeFuture; |
||||
|
switch (actionType) { |
||||
|
case ASSIGNED_TO_CUSTOMER: |
||||
|
CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class); |
||||
|
edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId); |
||||
|
Futures.addCallback(edgeFuture, new FutureCallback<Edge>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable Edge edge) { |
||||
|
if (edge != null && !customerId.isNullUid()) { |
||||
|
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.ADDED, customerId, null); |
||||
|
PageLink pageLink = new PageLink(DEFAULT_LIMIT); |
||||
|
PageData<User> pageData; |
||||
|
do { |
||||
|
pageData = userService.findCustomerUsers(tenantId, customerId, pageLink); |
||||
|
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { |
||||
|
log.trace("[{}] [{}] user(s) are going to be added to edge.", edge.getId(), pageData.getData().size()); |
||||
|
for (User user : pageData.getData()) { |
||||
|
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.USER, EdgeEventActionType.ADDED, user.getId(), null); |
||||
|
} |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} |
||||
|
} while (pageData != null && pageData.hasNext()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.error("Can't find edge by id [{}]", edgeNotificationMsg, t); |
||||
|
} |
||||
|
}, dbCallbackExecutorService); |
||||
|
break; |
||||
|
case UNASSIGNED_FROM_CUSTOMER: |
||||
|
CustomerId customerIdToDelete = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class); |
||||
|
edgeFuture = edgeService.findEdgeByIdAsync(tenantId, edgeId); |
||||
|
Futures.addCallback(edgeFuture, new FutureCallback<Edge>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable Edge edge) { |
||||
|
if (edge != null && !customerIdToDelete.isNullUid()) { |
||||
|
saveEdgeEvent(edge.getTenantId(), edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.DELETED, customerIdToDelete, null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.error("Can't find edge by id [{}]", edgeNotificationMsg, t); |
||||
|
} |
||||
|
}, dbCallbackExecutorService); |
||||
|
break; |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
log.error("Exception during processing edge event", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void processWidgetBundleOrWidgetType(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { |
||||
|
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()); |
||||
|
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); |
||||
|
EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); |
||||
|
switch (actionType) { |
||||
|
case ADDED: |
||||
|
case UPDATED: |
||||
|
case DELETED: |
||||
|
processActionForAllEdges(tenantId, type, actionType, entityId); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void processCustomer(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { |
||||
|
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()); |
||||
|
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); |
||||
|
UUID uuid = new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB()); |
||||
|
CustomerId customerId = new CustomerId(EntityIdFactory.getByEdgeEventTypeAndUuid(type, uuid).getId()); |
||||
|
switch (actionType) { |
||||
|
case UPDATED: |
||||
|
PageLink pageLink = new PageLink(DEFAULT_LIMIT); |
||||
|
PageData<Edge> pageData; |
||||
|
do { |
||||
|
pageData = edgeService.findEdgesByTenantIdAndCustomerId(tenantId, customerId, pageLink); |
||||
|
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { |
||||
|
for (Edge edge : pageData.getData()) { |
||||
|
saveEdgeEvent(tenantId, edge.getId(), type, actionType, customerId, null); |
||||
|
} |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} |
||||
|
} while (pageData != null && pageData.hasNext()); |
||||
|
break; |
||||
|
case DELETED: |
||||
|
EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB())); |
||||
|
saveEdgeEvent(tenantId, edgeId, type, actionType, customerId, null); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void processEntity(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { |
||||
|
EdgeEventActionType actionType = EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()); |
||||
|
EdgeEventType type = EdgeEventType.valueOf(edgeNotificationMsg.getType()); |
||||
|
EntityId entityId = EntityIdFactory.getByEdgeEventTypeAndUuid(type, |
||||
|
new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); |
||||
|
EdgeId edgeId = new EdgeId(new UUID(edgeNotificationMsg.getEdgeIdMSB(), edgeNotificationMsg.getEdgeIdLSB())); |
||||
|
ListenableFuture<List<EdgeId>> edgeIdsFuture; |
||||
|
switch (actionType) { |
||||
|
case ADDED: // used only for USER entity
|
||||
|
case UPDATED: |
||||
|
case CREDENTIALS_UPDATED: |
||||
|
edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId); |
||||
|
Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable List<EdgeId> edgeIds) { |
||||
|
if (edgeIds != null && !edgeIds.isEmpty()) { |
||||
|
for (EdgeId edgeId : edgeIds) { |
||||
|
saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
@Override |
||||
|
public void onFailure(Throwable throwable) { |
||||
|
log.error("Failed to find related edge ids [{}]", edgeNotificationMsg, throwable); |
||||
|
} |
||||
|
}, dbCallbackExecutorService); |
||||
|
break; |
||||
|
case ASSIGNED_TO_CUSTOMER: |
||||
|
case UNASSIGNED_FROM_CUSTOMER: |
||||
|
edgeIdsFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, entityId); |
||||
|
Futures.addCallback(edgeIdsFuture, new FutureCallback<List<EdgeId>>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable List<EdgeId> edgeIds) { |
||||
|
if (edgeIds != null && !edgeIds.isEmpty()) { |
||||
|
for (EdgeId edgeId : edgeIds) { |
||||
|
try { |
||||
|
CustomerId customerId = mapper.readValue(edgeNotificationMsg.getBody(), CustomerId.class); |
||||
|
ListenableFuture<Edge> future = edgeService.findEdgeByIdAsync(tenantId, edgeId); |
||||
|
Futures.addCallback(future, new FutureCallback<Edge>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable Edge edge) { |
||||
|
if (edge != null && edge.getCustomerId() != null && |
||||
|
!edge.getCustomerId().isNullUid() && edge.getCustomerId().equals(customerId)) { |
||||
|
saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null); |
||||
|
} |
||||
|
} |
||||
|
@Override |
||||
|
public void onFailure(Throwable throwable) { |
||||
|
log.error("Failed to find edge by id [{}]", edgeNotificationMsg, throwable); |
||||
|
} |
||||
|
}, dbCallbackExecutorService); |
||||
|
} catch (Exception e) { |
||||
|
log.error("Can't parse customer id from entity body [{}]", edgeNotificationMsg, e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable throwable) { |
||||
|
log.error("Failed to find related edge ids [{}]", edgeNotificationMsg, throwable); |
||||
|
} |
||||
|
}, dbCallbackExecutorService); |
||||
|
break; |
||||
|
case DELETED: |
||||
|
saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null); |
||||
|
break; |
||||
|
case ASSIGNED_TO_EDGE: |
||||
|
case UNASSIGNED_FROM_EDGE: |
||||
|
saveEdgeEvent(tenantId, edgeId, type, actionType, entityId, null); |
||||
|
if (type.equals(EdgeEventType.RULE_CHAIN)) { |
||||
|
updateDependentRuleChains(tenantId, new RuleChainId(entityId.getId()), edgeId); |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void updateDependentRuleChains(TenantId tenantId, RuleChainId processingRuleChainId, EdgeId edgeId) { |
||||
|
PageLink pageLink = new PageLink(DEFAULT_LIMIT); |
||||
|
PageData<RuleChain> pageData; |
||||
|
do { |
||||
|
pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edgeId, pageLink); |
||||
|
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { |
||||
|
for (RuleChain ruleChain : pageData.getData()) { |
||||
|
if (!ruleChain.getId().equals(processingRuleChainId)) { |
||||
|
List<RuleChainConnectionInfo> connectionInfos = |
||||
|
ruleChainService.loadRuleChainMetaData(ruleChain.getTenantId(), ruleChain.getId()).getRuleChainConnections(); |
||||
|
if (connectionInfos != null && !connectionInfos.isEmpty()) { |
||||
|
for (RuleChainConnectionInfo connectionInfo : connectionInfos) { |
||||
|
if (connectionInfo.getTargetRuleChainId().equals(processingRuleChainId)) { |
||||
|
saveEdgeEvent(tenantId, |
||||
|
edgeId, |
||||
|
EdgeEventType.RULE_CHAIN_METADATA, |
||||
|
EdgeEventActionType.UPDATED, |
||||
|
ruleChain.getId(), |
||||
|
null); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} |
||||
|
} while (pageData != null && pageData.hasNext()); |
||||
|
} |
||||
|
|
||||
|
private void processAlarm(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { |
||||
|
AlarmId alarmId = new AlarmId(new UUID(edgeNotificationMsg.getEntityIdMSB(), edgeNotificationMsg.getEntityIdLSB())); |
||||
|
ListenableFuture<Alarm> alarmFuture = alarmService.findAlarmByIdAsync(tenantId, alarmId); |
||||
|
Futures.addCallback(alarmFuture, new FutureCallback<Alarm>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable Alarm alarm) { |
||||
|
if (alarm != null) { |
||||
|
EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(alarm.getOriginator().getEntityType()); |
||||
|
if (type != null) { |
||||
|
ListenableFuture<List<EdgeId>> relatedEdgeIdsByEntityIdFuture = edgeService.findRelatedEdgeIdsByEntityId(tenantId, alarm.getOriginator()); |
||||
|
Futures.addCallback(relatedEdgeIdsByEntityIdFuture, new FutureCallback<List<EdgeId>>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable List<EdgeId> relatedEdgeIdsByEntityId) { |
||||
|
if (relatedEdgeIdsByEntityId != null) { |
||||
|
for (EdgeId edgeId : relatedEdgeIdsByEntityId) { |
||||
|
saveEdgeEvent(tenantId, |
||||
|
edgeId, |
||||
|
EdgeEventType.ALARM, |
||||
|
EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()), |
||||
|
alarmId, |
||||
|
null); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.warn("[{}] can't find related edge ids by entity id [{}]", tenantId.getId(), alarm.getOriginator(), t); |
||||
|
} |
||||
|
}, dbCallbackExecutorService); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.warn("[{}] can't find alarm by id [{}]", tenantId.getId(), alarmId.getId(), t); |
||||
|
} |
||||
|
}, dbCallbackExecutorService); |
||||
|
} |
||||
|
|
||||
|
private void processRelation(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) throws JsonProcessingException { |
||||
|
EntityRelation relation = mapper.readValue(edgeNotificationMsg.getBody(), EntityRelation.class); |
||||
|
if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) && |
||||
|
!relation.getTo().getEntityType().equals(EntityType.EDGE)) { |
||||
|
List<ListenableFuture<List<EdgeId>>> futures = new ArrayList<>(); |
||||
|
futures.add(edgeService.findRelatedEdgeIdsByEntityId(tenantId, relation.getTo())); |
||||
|
futures.add(edgeService.findRelatedEdgeIdsByEntityId(tenantId, relation.getFrom())); |
||||
|
ListenableFuture<List<List<EdgeId>>> combinedFuture = Futures.allAsList(futures); |
||||
|
Futures.addCallback(combinedFuture, new FutureCallback<List<List<EdgeId>>>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable List<List<EdgeId>> listOfListsEdgeIds) { |
||||
|
Set<EdgeId> uniqueEdgeIds = new HashSet<>(); |
||||
|
if (listOfListsEdgeIds != null && !listOfListsEdgeIds.isEmpty()) { |
||||
|
for (List<EdgeId> listOfListsEdgeId : listOfListsEdgeIds) { |
||||
|
if (listOfListsEdgeId != null) { |
||||
|
uniqueEdgeIds.addAll(listOfListsEdgeId); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
if (!uniqueEdgeIds.isEmpty()) { |
||||
|
for (EdgeId edgeId : uniqueEdgeIds) { |
||||
|
saveEdgeEvent(tenantId, |
||||
|
edgeId, |
||||
|
EdgeEventType.RELATION, |
||||
|
EdgeEventActionType.valueOf(edgeNotificationMsg.getAction()), |
||||
|
null, |
||||
|
mapper.valueToTree(relation)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.warn("[{}] can't find related edge ids by relation to id [{}] and relation from id [{}]" , |
||||
|
tenantId.getId(), relation.getTo().getId(), relation.getFrom().getId(), t); |
||||
|
} |
||||
|
}, dbCallbackExecutorService); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void processActionForAllEdges(TenantId tenantId, EdgeEventType type, EdgeEventActionType actionType, EntityId entityId) { |
||||
|
PageLink pageLink = new PageLink(DEFAULT_LIMIT); |
||||
|
PageData<Edge> pageData; |
||||
|
do { |
||||
|
pageData = edgeService.findEdgesByTenantId(tenantId, pageLink); |
||||
|
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { |
||||
|
for (Edge edge : pageData.getData()) { |
||||
|
saveEdgeEvent(tenantId, edge.getId(), type, actionType, entityId, null); |
||||
|
} |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} |
||||
|
} while (pageData != null && pageData.hasNext()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
@ -0,0 +1,233 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import lombok.Getter; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.context.annotation.Lazy; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.actors.service.ActorService; |
||||
|
import org.thingsboard.server.dao.alarm.AlarmService; |
||||
|
import org.thingsboard.server.dao.asset.AssetService; |
||||
|
import org.thingsboard.server.dao.attributes.AttributesService; |
||||
|
import org.thingsboard.server.dao.customer.CustomerService; |
||||
|
import org.thingsboard.server.dao.dashboard.DashboardService; |
||||
|
import org.thingsboard.server.dao.device.DeviceCredentialsService; |
||||
|
import org.thingsboard.server.dao.device.DeviceProfileService; |
||||
|
import org.thingsboard.server.dao.device.DeviceService; |
||||
|
import org.thingsboard.server.dao.edge.EdgeEventService; |
||||
|
import org.thingsboard.server.dao.edge.EdgeService; |
||||
|
import org.thingsboard.server.dao.entityview.EntityViewService; |
||||
|
import org.thingsboard.server.dao.relation.RelationService; |
||||
|
import org.thingsboard.server.dao.rule.RuleChainService; |
||||
|
import org.thingsboard.server.dao.user.UserService; |
||||
|
import org.thingsboard.server.dao.widget.WidgetTypeService; |
||||
|
import org.thingsboard.server.dao.widget.WidgetsBundleService; |
||||
|
import org.thingsboard.server.queue.discovery.PartitionService; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.edge.rpc.EdgeEventStorageSettings; |
||||
|
import org.thingsboard.server.service.edge.rpc.constructor.AdminSettingsMsgConstructor; |
||||
|
import org.thingsboard.server.service.edge.rpc.constructor.AlarmMsgConstructor; |
||||
|
import org.thingsboard.server.service.edge.rpc.constructor.AssetMsgConstructor; |
||||
|
import org.thingsboard.server.service.edge.rpc.constructor.CustomerMsgConstructor; |
||||
|
import org.thingsboard.server.service.edge.rpc.constructor.DashboardMsgConstructor; |
||||
|
import org.thingsboard.server.service.edge.rpc.constructor.DeviceMsgConstructor; |
||||
|
import org.thingsboard.server.service.edge.rpc.constructor.DeviceProfileMsgConstructor; |
||||
|
import org.thingsboard.server.service.edge.rpc.constructor.EntityDataMsgConstructor; |
||||
|
import org.thingsboard.server.service.edge.rpc.constructor.EntityViewMsgConstructor; |
||||
|
import org.thingsboard.server.service.edge.rpc.constructor.RelationMsgConstructor; |
||||
|
import org.thingsboard.server.service.edge.rpc.constructor.RuleChainMsgConstructor; |
||||
|
import org.thingsboard.server.service.edge.rpc.constructor.UserMsgConstructor; |
||||
|
import org.thingsboard.server.service.edge.rpc.constructor.WidgetTypeMsgConstructor; |
||||
|
import org.thingsboard.server.service.edge.rpc.constructor.WidgetsBundleMsgConstructor; |
||||
|
import org.thingsboard.server.service.edge.rpc.init.SyncEdgeService; |
||||
|
import org.thingsboard.server.service.edge.rpc.processor.AlarmProcessor; |
||||
|
import org.thingsboard.server.service.edge.rpc.processor.DeviceProcessor; |
||||
|
import org.thingsboard.server.service.edge.rpc.processor.RelationProcessor; |
||||
|
import org.thingsboard.server.service.edge.rpc.processor.TelemetryProcessor; |
||||
|
import org.thingsboard.server.service.executors.DbCallbackExecutorService; |
||||
|
import org.thingsboard.server.service.queue.TbClusterService; |
||||
|
import org.thingsboard.server.service.state.DeviceStateService; |
||||
|
|
||||
|
@Component |
||||
|
@TbCoreComponent |
||||
|
@Data |
||||
|
public class EdgeContextComponent { |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private EdgeService edgeService; |
||||
|
|
||||
|
@Autowired |
||||
|
private PartitionService partitionService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private EdgeEventService edgeEventService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private AssetService assetService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private DeviceService deviceService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private DeviceProfileService deviceProfileService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private DeviceCredentialsService deviceCredentialsService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private EntityViewService entityViewService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private AttributesService attributesService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private CustomerService customerService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private RelationService relationService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private AlarmService alarmService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private DashboardService dashboardService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private RuleChainService ruleChainService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private UserService userService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private ActorService actorService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private WidgetsBundleService widgetsBundleService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private WidgetTypeService widgetTypeService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private DeviceStateService deviceStateService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private TbClusterService tbClusterService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private SyncEdgeService syncEdgeService; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private RuleChainMsgConstructor ruleChainMsgConstructor; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private AlarmMsgConstructor alarmMsgConstructor; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private DeviceMsgConstructor deviceMsgConstructor; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private DeviceProfileMsgConstructor deviceProfileMsgConstructor; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private AssetMsgConstructor assetMsgConstructor; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private EntityViewMsgConstructor entityViewMsgConstructor; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private DashboardMsgConstructor dashboardMsgConstructor; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private CustomerMsgConstructor customerMsgConstructor; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private UserMsgConstructor userMsgConstructor; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private RelationMsgConstructor relationMsgConstructor; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private WidgetsBundleMsgConstructor widgetsBundleMsgConstructor; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private WidgetTypeMsgConstructor widgetTypeMsgConstructor; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private AdminSettingsMsgConstructor adminSettingsMsgConstructor; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private EntityDataMsgConstructor entityDataMsgConstructor; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private AlarmProcessor alarmProcessor; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private DeviceProcessor deviceProcessor; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private RelationProcessor relationProcessor; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private TelemetryProcessor telemetryProcessor; |
||||
|
|
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private EdgeEventStorageSettings edgeEventStorageSettings; |
||||
|
|
||||
|
@Autowired |
||||
|
@Getter |
||||
|
private DbCallbackExecutorService dbCallbackExecutor; |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge; |
||||
|
|
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.common.data.id.RuleChainId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.msg.queue.TbCallback; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos; |
||||
|
|
||||
|
import java.io.IOException; |
||||
|
|
||||
|
public interface EdgeNotificationService { |
||||
|
|
||||
|
Edge setEdgeRootRuleChain(TenantId tenantId, Edge edge, RuleChainId ruleChainId) throws IOException; |
||||
|
|
||||
|
void pushNotificationToEdge(TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg, TbCallback callback); |
||||
|
} |
||||
@ -0,0 +1,32 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc; |
||||
|
|
||||
|
|
||||
|
import lombok.Data; |
||||
|
import org.springframework.beans.factory.annotation.Value; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
|
||||
|
@Component |
||||
|
@Data |
||||
|
public class EdgeEventStorageSettings { |
||||
|
@Value("${edges.storage.max_read_records_count}") |
||||
|
private int maxReadRecordsCount; |
||||
|
@Value("${edges.storage.no_read_records_sleep}") |
||||
|
private long noRecordsSleepInterval; |
||||
|
@Value("${edges.storage.sleep_between_batches}") |
||||
|
private long sleepIntervalBetweenBatches; |
||||
|
} |
||||
@ -0,0 +1,290 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc; |
||||
|
|
||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
|
import com.google.common.io.Resources; |
||||
|
import com.google.common.util.concurrent.FutureCallback; |
||||
|
import io.grpc.Server; |
||||
|
import io.grpc.netty.NettyServerBuilder; |
||||
|
import io.grpc.stub.StreamObserver; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.beans.factory.annotation.Value; |
||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.common.util.ThingsBoardThreadFactory; |
||||
|
import org.thingsboard.server.common.data.DataConstants; |
||||
|
import org.thingsboard.server.common.data.Tenant; |
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.common.data.id.EdgeId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
||||
|
import org.thingsboard.server.common.data.kv.BooleanDataEntry; |
||||
|
import org.thingsboard.server.common.data.kv.LongDataEntry; |
||||
|
import org.thingsboard.server.gen.edge.EdgeRpcServiceGrpc; |
||||
|
import org.thingsboard.server.gen.edge.RequestMsg; |
||||
|
import org.thingsboard.server.gen.edge.ResponseMsg; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.edge.EdgeContextComponent; |
||||
|
import org.thingsboard.server.service.state.DefaultDeviceStateService; |
||||
|
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; |
||||
|
|
||||
|
import javax.annotation.Nullable; |
||||
|
import javax.annotation.PostConstruct; |
||||
|
import javax.annotation.PreDestroy; |
||||
|
import java.io.File; |
||||
|
import java.io.IOException; |
||||
|
import java.util.Collections; |
||||
|
import java.util.Map; |
||||
|
import java.util.UUID; |
||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||
|
import java.util.concurrent.ConcurrentMap; |
||||
|
import java.util.concurrent.Executors; |
||||
|
import java.util.concurrent.ScheduledExecutorService; |
||||
|
import java.util.concurrent.ScheduledFuture; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
@Service |
||||
|
@Slf4j |
||||
|
@ConditionalOnProperty(prefix = "edges", value = "enabled", havingValue = "true") |
||||
|
@TbCoreComponent |
||||
|
public class EdgeGrpcService extends EdgeRpcServiceGrpc.EdgeRpcServiceImplBase implements EdgeRpcService { |
||||
|
|
||||
|
private final ConcurrentMap<EdgeId, EdgeGrpcSession> sessions = new ConcurrentHashMap<>(); |
||||
|
private final ConcurrentMap<EdgeId, Boolean> sessionNewEvents = new ConcurrentHashMap<>(); |
||||
|
private final ConcurrentMap<EdgeId, ScheduledFuture<?>> sessionEdgeEventChecks = new ConcurrentHashMap<>(); |
||||
|
private static final ObjectMapper mapper = new ObjectMapper(); |
||||
|
|
||||
|
@Value("${edges.rpc.port}") |
||||
|
private int rpcPort; |
||||
|
@Value("${edges.rpc.ssl.enabled}") |
||||
|
private boolean sslEnabled; |
||||
|
@Value("${edges.rpc.ssl.cert}") |
||||
|
private String certFileResource; |
||||
|
@Value("${edges.rpc.ssl.private_key}") |
||||
|
private String privateKeyResource; |
||||
|
@Value("${edges.state.persistToTelemetry:false}") |
||||
|
private boolean persistToTelemetry; |
||||
|
@Value("${edges.rpc.client_max_keep_alive_time_sec}") |
||||
|
private int clientMaxKeepAliveTimeSec; |
||||
|
|
||||
|
@Value("${edges.scheduler_pool_size}") |
||||
|
private int schedulerPoolSize; |
||||
|
|
||||
|
@Autowired |
||||
|
private EdgeContextComponent ctx; |
||||
|
|
||||
|
@Autowired |
||||
|
private TelemetrySubscriptionService tsSubService; |
||||
|
|
||||
|
private Server server; |
||||
|
|
||||
|
private ScheduledExecutorService scheduler; |
||||
|
|
||||
|
@PostConstruct |
||||
|
public void init() { |
||||
|
log.info("Initializing Edge RPC service!"); |
||||
|
NettyServerBuilder builder = NettyServerBuilder.forPort(rpcPort) |
||||
|
.permitKeepAliveTime(clientMaxKeepAliveTimeSec, TimeUnit.SECONDS) |
||||
|
.addService(this); |
||||
|
if (sslEnabled) { |
||||
|
try { |
||||
|
File certFile = new File(Resources.getResource(certFileResource).toURI()); |
||||
|
File privateKeyFile = new File(Resources.getResource(privateKeyResource).toURI()); |
||||
|
builder.useTransportSecurity(certFile, privateKeyFile); |
||||
|
} catch (Exception e) { |
||||
|
log.error("Unable to set up SSL context. Reason: " + e.getMessage(), e); |
||||
|
throw new RuntimeException("Unable to set up SSL context!", e); |
||||
|
} |
||||
|
} |
||||
|
server = builder.build(); |
||||
|
log.info("Going to start Edge RPC server using port: {}", rpcPort); |
||||
|
try { |
||||
|
server.start(); |
||||
|
} catch (IOException e) { |
||||
|
log.error("Failed to start Edge RPC server!", e); |
||||
|
throw new RuntimeException("Failed to start Edge RPC server!"); |
||||
|
} |
||||
|
this.scheduler = Executors.newScheduledThreadPool(schedulerPoolSize, ThingsBoardThreadFactory.forName("edge-scheduler")); |
||||
|
log.info("Edge RPC service initialized!"); |
||||
|
} |
||||
|
|
||||
|
@PreDestroy |
||||
|
public void destroy() { |
||||
|
if (server != null) { |
||||
|
server.shutdownNow(); |
||||
|
} |
||||
|
for (Map.Entry<EdgeId, ScheduledFuture<?>> entry : sessionEdgeEventChecks.entrySet()) { |
||||
|
EdgeId edgeId = entry.getKey(); |
||||
|
ScheduledFuture<?> sessionEdgeEventCheck = entry.getValue(); |
||||
|
if (sessionEdgeEventCheck != null && !sessionEdgeEventCheck.isCancelled() && !sessionEdgeEventCheck.isDone()) { |
||||
|
sessionEdgeEventCheck.cancel(true); |
||||
|
sessionEdgeEventChecks.remove(edgeId); |
||||
|
} |
||||
|
} |
||||
|
if (scheduler != null) { |
||||
|
scheduler.shutdownNow(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public StreamObserver<RequestMsg> handleMsgs(StreamObserver<ResponseMsg> outputStream) { |
||||
|
return new EdgeGrpcSession(ctx, outputStream, this::onEdgeConnect, this::onEdgeDisconnect, mapper).getInputStream(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void updateEdge(Edge edge) { |
||||
|
EdgeGrpcSession session = sessions.get(edge.getId()); |
||||
|
if (session != null && session.isConnected()) { |
||||
|
log.debug("[{}] Updating configuration for edge [{}] [{}]", edge.getTenantId(), edge.getName(), edge.getId()); |
||||
|
session.onConfigurationUpdate(edge); |
||||
|
} else { |
||||
|
log.debug("[{}] Session doesn't exist for edge [{}] [{}]", edge.getTenantId(), edge.getName(), edge.getId()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void deleteEdge(EdgeId edgeId) { |
||||
|
EdgeGrpcSession session = sessions.get(edgeId); |
||||
|
if (session != null && session.isConnected()) { |
||||
|
log.info("Closing and removing session for edge [{}]", edgeId); |
||||
|
session.close(); |
||||
|
sessions.remove(edgeId); |
||||
|
sessionNewEvents.remove(edgeId); |
||||
|
cancelScheduleEdgeEventsCheck(edgeId); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onEdgeEvent(EdgeId edgeId) { |
||||
|
log.trace("[{}] onEdgeEvent", edgeId.getId()); |
||||
|
if (!sessionNewEvents.get(edgeId)) { |
||||
|
log.trace("[{}] set session new events flag to true", edgeId.getId()); |
||||
|
sessionNewEvents.put(edgeId, true); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void onEdgeConnect(EdgeId edgeId, EdgeGrpcSession edgeGrpcSession) { |
||||
|
log.info("[{}] edge [{}] connected successfully.", edgeGrpcSession.getSessionId(), edgeId); |
||||
|
sessions.put(edgeId, edgeGrpcSession); |
||||
|
sessionNewEvents.put(edgeId, false); |
||||
|
save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, true); |
||||
|
save(edgeId, DefaultDeviceStateService.LAST_CONNECT_TIME, System.currentTimeMillis()); |
||||
|
cancelScheduleEdgeEventsCheck(edgeId); |
||||
|
scheduleEdgeEventsCheck(edgeGrpcSession); |
||||
|
} |
||||
|
|
||||
|
public EdgeGrpcSession getEdgeGrpcSessionById(TenantId tenantId, EdgeId edgeId) { |
||||
|
EdgeGrpcSession session = sessions.get(edgeId); |
||||
|
if (session != null && session.isConnected()) { |
||||
|
return session; |
||||
|
} else { |
||||
|
log.error("[{}] Edge is not connected [{}]", tenantId, edgeId); |
||||
|
throw new RuntimeException("Edge is not connected"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void scheduleEdgeEventsCheck(EdgeGrpcSession session) { |
||||
|
EdgeId edgeId = session.getEdge().getId(); |
||||
|
UUID tenantId = session.getEdge().getTenantId().getId(); |
||||
|
if (sessions.containsKey(edgeId)) { |
||||
|
ScheduledFuture<?> schedule = scheduler.schedule(() -> { |
||||
|
try { |
||||
|
if (sessionNewEvents.get(edgeId)) { |
||||
|
log.trace("[{}] Set session new events flag to false", edgeId.getId()); |
||||
|
sessionNewEvents.put(edgeId, false); |
||||
|
session.processEdgeEvents(); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
log.warn("[{}] Failed to process edge events for edge [{}]!", tenantId, session.getEdge().getId().getId(), e); |
||||
|
} |
||||
|
scheduleEdgeEventsCheck(session); |
||||
|
}, ctx.getEdgeEventStorageSettings().getNoRecordsSleepInterval(), TimeUnit.MILLISECONDS); |
||||
|
sessionEdgeEventChecks.put(edgeId, schedule); |
||||
|
log.trace("[{}] Check edge event scheduled for edge [{}]", tenantId, edgeId.getId()); |
||||
|
} else { |
||||
|
log.debug("[{}] Session was removed and edge event check schedule must not be started [{}]", |
||||
|
tenantId, edgeId.getId()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void cancelScheduleEdgeEventsCheck(EdgeId edgeId) { |
||||
|
log.trace("[{}] cancelling edge event check for edge", edgeId); |
||||
|
if (sessionEdgeEventChecks.containsKey(edgeId)) { |
||||
|
ScheduledFuture<?> sessionEdgeEventCheck = sessionEdgeEventChecks.get(edgeId); |
||||
|
if (sessionEdgeEventCheck != null && !sessionEdgeEventCheck.isCancelled() && !sessionEdgeEventCheck.isDone()) { |
||||
|
sessionEdgeEventCheck.cancel(true); |
||||
|
sessionEdgeEventChecks.remove(edgeId); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void onEdgeDisconnect(EdgeId edgeId) { |
||||
|
log.info("[{}] edge disconnected!", edgeId); |
||||
|
sessions.remove(edgeId); |
||||
|
sessionNewEvents.remove(edgeId); |
||||
|
save(edgeId, DefaultDeviceStateService.ACTIVITY_STATE, false); |
||||
|
save(edgeId, DefaultDeviceStateService.LAST_DISCONNECT_TIME, System.currentTimeMillis()); |
||||
|
cancelScheduleEdgeEventsCheck(edgeId); |
||||
|
} |
||||
|
|
||||
|
private void save(EdgeId edgeId, String key, long value) { |
||||
|
log.debug("[{}] Updating long edge telemetry [{}] [{}]", edgeId, key, value); |
||||
|
if (persistToTelemetry) { |
||||
|
tsSubService.saveAndNotify( |
||||
|
TenantId.SYS_TENANT_ID, edgeId, |
||||
|
Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new LongDataEntry(key, value))), |
||||
|
new AttributeSaveCallback(edgeId, key, value)); |
||||
|
} else { |
||||
|
tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, edgeId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(edgeId, key, value)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void save(EdgeId edgeId, String key, boolean value) { |
||||
|
log.debug("[{}] Updating boolean edge telemetry [{}] [{}]", edgeId, key, value); |
||||
|
if (persistToTelemetry) { |
||||
|
tsSubService.saveAndNotify( |
||||
|
TenantId.SYS_TENANT_ID, edgeId, |
||||
|
Collections.singletonList(new BasicTsKvEntry(System.currentTimeMillis(), new BooleanDataEntry(key, value))), |
||||
|
new AttributeSaveCallback(edgeId, key, value)); |
||||
|
} else { |
||||
|
tsSubService.saveAttrAndNotify(TenantId.SYS_TENANT_ID, edgeId, DataConstants.SERVER_SCOPE, key, value, new AttributeSaveCallback(edgeId, key, value)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static class AttributeSaveCallback implements FutureCallback<Void> { |
||||
|
private final EdgeId edgeId; |
||||
|
private final String key; |
||||
|
private final Object value; |
||||
|
|
||||
|
AttributeSaveCallback(EdgeId edgeId, String key, Object value) { |
||||
|
this.edgeId = edgeId; |
||||
|
this.key = key; |
||||
|
this.value = value; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onSuccess(@Nullable Void result) { |
||||
|
log.trace("[{}] Successfully updated attribute [{}] with value [{}]", edgeId, key, value); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.warn("[{}] Failed to update attribute [{}] with value [{}]", edgeId, key, value, t); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
File diff suppressed because it is too large
@ -0,0 +1,28 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc; |
||||
|
|
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.common.data.id.EdgeId; |
||||
|
|
||||
|
public interface EdgeRpcService { |
||||
|
|
||||
|
void updateEdge(Edge edge); |
||||
|
|
||||
|
void deleteEdge(EdgeId edgeId); |
||||
|
|
||||
|
void onEdgeEvent(EdgeId edgeId); |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.constructor; |
||||
|
|
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.AdminSettings; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.gen.edge.AdminSettingsUpdateMsg; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
@Component |
||||
|
@TbCoreComponent |
||||
|
public class AdminSettingsMsgConstructor { |
||||
|
|
||||
|
public AdminSettingsUpdateMsg constructAdminSettingsUpdateMsg(AdminSettings adminSettings) { |
||||
|
AdminSettingsUpdateMsg.Builder builder = AdminSettingsUpdateMsg.newBuilder() |
||||
|
.setKey(adminSettings.getKey()) |
||||
|
.setJsonValue(JacksonUtil.toString(adminSettings.getJsonValue())); |
||||
|
if (adminSettings.getId() != null) { |
||||
|
builder.setIsSystem(true); |
||||
|
} |
||||
|
return builder.build(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,76 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.constructor; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.alarm.Alarm; |
||||
|
import org.thingsboard.server.common.data.id.AssetId; |
||||
|
import org.thingsboard.server.common.data.id.DeviceId; |
||||
|
import org.thingsboard.server.common.data.id.EntityViewId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.dao.asset.AssetService; |
||||
|
import org.thingsboard.server.dao.device.DeviceService; |
||||
|
import org.thingsboard.server.dao.entityview.EntityViewService; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.gen.edge.AlarmUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.UpdateMsgType; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
@Component |
||||
|
@TbCoreComponent |
||||
|
public class AlarmMsgConstructor { |
||||
|
|
||||
|
@Autowired |
||||
|
private DeviceService deviceService; |
||||
|
|
||||
|
@Autowired |
||||
|
private AssetService assetService; |
||||
|
|
||||
|
@Autowired |
||||
|
private EntityViewService entityViewService; |
||||
|
|
||||
|
public AlarmUpdateMsg constructAlarmUpdatedMsg(TenantId tenantId, UpdateMsgType msgType, Alarm alarm) { |
||||
|
String entityName = null; |
||||
|
switch (alarm.getOriginator().getEntityType()) { |
||||
|
case DEVICE: |
||||
|
entityName = deviceService.findDeviceById(tenantId, new DeviceId(alarm.getOriginator().getId())).getName(); |
||||
|
break; |
||||
|
case ASSET: |
||||
|
entityName = assetService.findAssetById(tenantId, new AssetId(alarm.getOriginator().getId())).getName(); |
||||
|
break; |
||||
|
case ENTITY_VIEW: |
||||
|
entityName = entityViewService.findEntityViewById(tenantId, new EntityViewId(alarm.getOriginator().getId())).getName(); |
||||
|
break; |
||||
|
} |
||||
|
AlarmUpdateMsg.Builder builder = AlarmUpdateMsg.newBuilder() |
||||
|
.setMsgType(msgType) |
||||
|
.setName(alarm.getName()) |
||||
|
.setType(alarm.getType()) |
||||
|
.setOriginatorName(entityName) |
||||
|
.setOriginatorType(alarm.getOriginator().getEntityType().name()) |
||||
|
.setSeverity(alarm.getSeverity().name()) |
||||
|
.setStatus(alarm.getStatus().name()) |
||||
|
.setStartTs(alarm.getStartTs()) |
||||
|
.setEndTs(alarm.getEndTs()) |
||||
|
.setAckTs(alarm.getAckTs()) |
||||
|
.setClearTs(alarm.getClearTs()) |
||||
|
.setDetails(JacksonUtil.toString(alarm.getDetails())) |
||||
|
.setPropagate(alarm.isPropagate()); |
||||
|
return builder.build(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,57 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.constructor; |
||||
|
|
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.asset.Asset; |
||||
|
import org.thingsboard.server.common.data.id.AssetId; |
||||
|
import org.thingsboard.server.common.data.id.CustomerId; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.gen.edge.AssetUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.UpdateMsgType; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
@Component |
||||
|
@TbCoreComponent |
||||
|
public class AssetMsgConstructor { |
||||
|
|
||||
|
public AssetUpdateMsg constructAssetUpdatedMsg(UpdateMsgType msgType, Asset asset, CustomerId customerId) { |
||||
|
AssetUpdateMsg.Builder builder = AssetUpdateMsg.newBuilder() |
||||
|
.setMsgType(msgType) |
||||
|
.setIdMSB(asset.getId().getId().getMostSignificantBits()) |
||||
|
.setIdLSB(asset.getId().getId().getLeastSignificantBits()) |
||||
|
.setName(asset.getName()) |
||||
|
.setType(asset.getType()); |
||||
|
if (asset.getLabel() != null) { |
||||
|
builder.setLabel(asset.getLabel()); |
||||
|
} |
||||
|
if (customerId != null) { |
||||
|
builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits()); |
||||
|
builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits()); |
||||
|
} |
||||
|
if (asset.getAdditionalInfo() != null) { |
||||
|
builder.setAdditionalInfo(JacksonUtil.toString(asset.getAdditionalInfo())); |
||||
|
} |
||||
|
return builder.build(); |
||||
|
} |
||||
|
|
||||
|
public AssetUpdateMsg constructAssetDeleteMsg(AssetId assetId) { |
||||
|
return AssetUpdateMsg.newBuilder() |
||||
|
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) |
||||
|
.setIdMSB(assetId.getId().getMostSignificantBits()) |
||||
|
.setIdLSB(assetId.getId().getLeastSignificantBits()).build(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,72 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.constructor; |
||||
|
|
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.Customer; |
||||
|
import org.thingsboard.server.common.data.id.CustomerId; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.gen.edge.CustomerUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.UpdateMsgType; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
@Component |
||||
|
@TbCoreComponent |
||||
|
public class CustomerMsgConstructor { |
||||
|
|
||||
|
public CustomerUpdateMsg constructCustomerUpdatedMsg(UpdateMsgType msgType, Customer customer) { |
||||
|
CustomerUpdateMsg.Builder builder = CustomerUpdateMsg.newBuilder() |
||||
|
.setMsgType(msgType) |
||||
|
.setIdMSB(customer.getId().getId().getMostSignificantBits()) |
||||
|
.setIdLSB(customer.getId().getId().getLeastSignificantBits()) |
||||
|
.setTitle(customer.getTitle()); |
||||
|
if (customer.getCountry() != null) { |
||||
|
builder.setCountry(customer.getCountry()); |
||||
|
} |
||||
|
if (customer.getState() != null) { |
||||
|
builder.setState(customer.getState()); |
||||
|
} |
||||
|
if (customer.getCity() != null) { |
||||
|
builder.setCity(customer.getCity()); |
||||
|
} |
||||
|
if (customer.getAddress() != null) { |
||||
|
builder.setAddress(customer.getAddress()); |
||||
|
} |
||||
|
if (customer.getAddress2() != null) { |
||||
|
builder.setAddress2(customer.getAddress2()); |
||||
|
} |
||||
|
if (customer.getZip() != null) { |
||||
|
builder.setZip(customer.getZip()); |
||||
|
} |
||||
|
if (customer.getPhone() != null) { |
||||
|
builder.setPhone(customer.getPhone()); |
||||
|
} |
||||
|
if (customer.getEmail() != null) { |
||||
|
builder.setEmail(customer.getEmail()); |
||||
|
} |
||||
|
if (customer.getAdditionalInfo() != null) { |
||||
|
builder.setAdditionalInfo(JacksonUtil.toString(customer.getAdditionalInfo())); |
||||
|
} |
||||
|
return builder.build(); |
||||
|
} |
||||
|
|
||||
|
public CustomerUpdateMsg constructCustomerDeleteMsg(CustomerId customerId) { |
||||
|
return CustomerUpdateMsg.newBuilder() |
||||
|
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) |
||||
|
.setIdMSB(customerId.getId().getMostSignificantBits()) |
||||
|
.setIdLSB(customerId.getId().getLeastSignificantBits()).build(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,52 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.constructor; |
||||
|
|
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.Dashboard; |
||||
|
import org.thingsboard.server.common.data.id.CustomerId; |
||||
|
import org.thingsboard.server.common.data.id.DashboardId; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.gen.edge.DashboardUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.UpdateMsgType; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
@Component |
||||
|
@TbCoreComponent |
||||
|
public class DashboardMsgConstructor { |
||||
|
|
||||
|
public DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard, CustomerId customerId) { |
||||
|
DashboardUpdateMsg.Builder builder = DashboardUpdateMsg.newBuilder() |
||||
|
.setMsgType(msgType) |
||||
|
.setIdMSB(dashboard.getId().getId().getMostSignificantBits()) |
||||
|
.setIdLSB(dashboard.getId().getId().getLeastSignificantBits()) |
||||
|
.setTitle(dashboard.getTitle()) |
||||
|
.setConfiguration(JacksonUtil.toString(dashboard.getConfiguration())); |
||||
|
if (customerId != null) { |
||||
|
builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits()); |
||||
|
builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits()); |
||||
|
} |
||||
|
return builder.build(); |
||||
|
} |
||||
|
|
||||
|
public DashboardUpdateMsg constructDashboardDeleteMsg(DashboardId dashboardId) { |
||||
|
return DashboardUpdateMsg.newBuilder() |
||||
|
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) |
||||
|
.setIdMSB(dashboardId.getId().getMostSignificantBits()) |
||||
|
.setIdLSB(dashboardId.getId().getLeastSignificantBits()).build(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,111 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.constructor; |
||||
|
|
||||
|
import com.fasterxml.jackson.databind.JsonNode; |
||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.id.CustomerId; |
||||
|
import org.thingsboard.server.common.data.id.DeviceId; |
||||
|
import org.thingsboard.server.common.data.security.DeviceCredentials; |
||||
|
import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.DeviceRpcCallMsg; |
||||
|
import org.thingsboard.server.gen.edge.DeviceUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.RpcRequestMsg; |
||||
|
import org.thingsboard.server.gen.edge.UpdateMsgType; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
import java.util.UUID; |
||||
|
|
||||
|
@Component |
||||
|
@TbCoreComponent |
||||
|
public class DeviceMsgConstructor { |
||||
|
|
||||
|
protected static final ObjectMapper mapper = new ObjectMapper(); |
||||
|
|
||||
|
public DeviceUpdateMsg constructDeviceUpdatedMsg(UpdateMsgType msgType, Device device, CustomerId customerId, String conflictName) { |
||||
|
DeviceUpdateMsg.Builder builder = DeviceUpdateMsg.newBuilder() |
||||
|
.setMsgType(msgType) |
||||
|
.setIdMSB(device.getId().getId().getMostSignificantBits()) |
||||
|
.setIdLSB(device.getId().getId().getLeastSignificantBits()) |
||||
|
.setName(device.getName()) |
||||
|
.setType(device.getType()); |
||||
|
if (device.getLabel() != null) { |
||||
|
builder.setLabel(device.getLabel()); |
||||
|
} |
||||
|
if (customerId != null) { |
||||
|
builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits()); |
||||
|
builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits()); |
||||
|
} |
||||
|
if (device.getDeviceProfileId() != null) { |
||||
|
builder.setDeviceProfileIdMSB(device.getDeviceProfileId().getId().getMostSignificantBits()); |
||||
|
builder.setDeviceProfileIdLSB(device.getDeviceProfileId().getId().getLeastSignificantBits()); |
||||
|
} |
||||
|
if (device.getAdditionalInfo() != null) { |
||||
|
builder.setAdditionalInfo(JacksonUtil.toString(device.getAdditionalInfo())); |
||||
|
} |
||||
|
if (conflictName != null) { |
||||
|
builder.setConflictName(conflictName); |
||||
|
} |
||||
|
return builder.build(); |
||||
|
} |
||||
|
|
||||
|
public DeviceCredentialsUpdateMsg constructDeviceCredentialsUpdatedMsg(DeviceCredentials deviceCredentials) { |
||||
|
DeviceCredentialsUpdateMsg.Builder builder = DeviceCredentialsUpdateMsg.newBuilder() |
||||
|
.setDeviceIdMSB(deviceCredentials.getDeviceId().getId().getMostSignificantBits()) |
||||
|
.setDeviceIdLSB(deviceCredentials.getDeviceId().getId().getLeastSignificantBits()); |
||||
|
if (deviceCredentials.getCredentialsType() != null) { |
||||
|
builder.setCredentialsType(deviceCredentials.getCredentialsType().name()) |
||||
|
.setCredentialsId(deviceCredentials.getCredentialsId()); |
||||
|
} |
||||
|
if (deviceCredentials.getCredentialsValue() != null) { |
||||
|
builder.setCredentialsValue(deviceCredentials.getCredentialsValue()); |
||||
|
} |
||||
|
return builder.build(); |
||||
|
} |
||||
|
|
||||
|
public DeviceUpdateMsg constructDeviceDeleteMsg(DeviceId deviceId) { |
||||
|
return DeviceUpdateMsg.newBuilder() |
||||
|
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) |
||||
|
.setIdMSB(deviceId.getId().getMostSignificantBits()) |
||||
|
.setIdLSB(deviceId.getId().getLeastSignificantBits()).build(); |
||||
|
} |
||||
|
|
||||
|
public DeviceRpcCallMsg constructDeviceRpcCallMsg(UUID deviceId, JsonNode body) { |
||||
|
int requestId = body.get("requestId").asInt(); |
||||
|
boolean oneway = body.get("oneway").asBoolean(); |
||||
|
UUID requestUUID = UUID.fromString(body.get("requestUUID").asText()); |
||||
|
long expirationTime = body.get("expirationTime").asLong(); |
||||
|
String method = body.get("method").asText(); |
||||
|
String params = body.get("params").asText(); |
||||
|
|
||||
|
RpcRequestMsg.Builder requestBuilder = RpcRequestMsg.newBuilder(); |
||||
|
requestBuilder.setMethod(method); |
||||
|
requestBuilder.setParams(params); |
||||
|
DeviceRpcCallMsg.Builder builder = DeviceRpcCallMsg.newBuilder() |
||||
|
.setDeviceIdMSB(deviceId.getMostSignificantBits()) |
||||
|
.setDeviceIdLSB(deviceId.getLeastSignificantBits()) |
||||
|
.setRequestUuidMSB(requestUUID.getMostSignificantBits()) |
||||
|
.setRequestUuidLSB(requestUUID.getLeastSignificantBits()) |
||||
|
.setRequestId(requestId) |
||||
|
.setExpirationTime(expirationTime) |
||||
|
.setOneway(oneway) |
||||
|
.setRequestMsg(requestBuilder.build()); |
||||
|
return builder.build(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,70 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.constructor; |
||||
|
|
||||
|
import com.google.protobuf.ByteString; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.DeviceProfile; |
||||
|
import org.thingsboard.server.common.data.id.DeviceProfileId; |
||||
|
import org.thingsboard.server.common.transport.util.DataDecodingEncodingService; |
||||
|
import org.thingsboard.server.gen.edge.DeviceProfileUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.UpdateMsgType; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
@Component |
||||
|
@TbCoreComponent |
||||
|
public class DeviceProfileMsgConstructor { |
||||
|
|
||||
|
@Autowired |
||||
|
private DataDecodingEncodingService dataDecodingEncodingService; |
||||
|
|
||||
|
public DeviceProfileUpdateMsg constructDeviceProfileUpdatedMsg(UpdateMsgType msgType, DeviceProfile deviceProfile) { |
||||
|
DeviceProfileUpdateMsg.Builder builder = DeviceProfileUpdateMsg.newBuilder() |
||||
|
.setMsgType(msgType) |
||||
|
.setIdMSB(deviceProfile.getId().getId().getMostSignificantBits()) |
||||
|
.setIdLSB(deviceProfile.getId().getId().getLeastSignificantBits()) |
||||
|
.setName(deviceProfile.getName()) |
||||
|
.setDefault(deviceProfile.isDefault()) |
||||
|
.setType(deviceProfile.getType().name()) |
||||
|
.setTransportType(deviceProfile.getTransportType().name()) |
||||
|
.setProvisionType(deviceProfile.getProvisionType().name()) |
||||
|
.setProfileDataBytes(ByteString.copyFrom(dataDecodingEncodingService.encode(deviceProfile.getProfileData()))); |
||||
|
// TODO: voba - should this be always null at the moment??
|
||||
|
// if (deviceProfile.getDefaultRuleChainId() != null) {
|
||||
|
// builder.setDefaultRuleChainIdMSB(deviceProfile.getDefaultRuleChainId().getId().getMostSignificantBits())
|
||||
|
// .setDefaultRuleChainIdLSB(deviceProfile.getDefaultRuleChainId().getId().getLeastSignificantBits());
|
||||
|
// }
|
||||
|
// if (deviceProfile.getDefaultQueueName() != null) {
|
||||
|
// builder.setDefaultQueueName(deviceProfile.getDefaultQueueName());
|
||||
|
// }
|
||||
|
if (deviceProfile.getDescription() != null) { |
||||
|
builder.setDescription(deviceProfile.getDescription()); |
||||
|
} |
||||
|
if (deviceProfile.getProvisionDeviceKey() != null) { |
||||
|
builder.setProvisionDeviceKey(deviceProfile.getProvisionDeviceKey()); |
||||
|
} |
||||
|
return builder.build(); |
||||
|
} |
||||
|
|
||||
|
public DeviceProfileUpdateMsg constructDeviceProfileDeleteMsg(DeviceProfileId deviceProfileId) { |
||||
|
return DeviceProfileUpdateMsg.newBuilder() |
||||
|
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) |
||||
|
.setIdMSB(deviceProfileId.getId().getMostSignificantBits()) |
||||
|
.setIdLSB(deviceProfileId.getId().getLeastSignificantBits()).build(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,97 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.constructor; |
||||
|
|
||||
|
import com.google.gson.Gson; |
||||
|
import com.google.gson.JsonArray; |
||||
|
import com.google.gson.JsonElement; |
||||
|
import com.google.gson.JsonObject; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.audit.ActionType; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
||||
|
import org.thingsboard.server.gen.edge.AttributeDeleteMsg; |
||||
|
import org.thingsboard.server.gen.edge.EntityDataProto; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
import java.util.List; |
||||
|
|
||||
|
@Component |
||||
|
@Slf4j |
||||
|
@TbCoreComponent |
||||
|
public class EntityDataMsgConstructor { |
||||
|
|
||||
|
public EntityDataProto constructEntityDataMsg(EntityId entityId, EdgeEventActionType actionType, JsonElement entityData) { |
||||
|
EntityDataProto.Builder builder = EntityDataProto.newBuilder() |
||||
|
.setEntityIdMSB(entityId.getId().getMostSignificantBits()) |
||||
|
.setEntityIdLSB(entityId.getId().getLeastSignificantBits()) |
||||
|
.setEntityType(entityId.getEntityType().name()); |
||||
|
switch (actionType) { |
||||
|
case TIMESERIES_UPDATED: |
||||
|
try { |
||||
|
JsonObject data = entityData.getAsJsonObject(); |
||||
|
long ts; |
||||
|
if (data.get("ts") != null && !data.get("ts").isJsonNull()) { |
||||
|
ts = data.getAsJsonPrimitive("ts").getAsLong(); |
||||
|
} else { |
||||
|
ts = System.currentTimeMillis(); |
||||
|
} |
||||
|
builder.setPostTelemetryMsg(JsonConverter.convertToTelemetryProto(data.getAsJsonObject("data"), ts)); |
||||
|
} catch (Exception e) { |
||||
|
log.warn("[{}] Can't convert to telemetry proto, entityData [{}]", entityId, entityData, e); |
||||
|
} |
||||
|
break; |
||||
|
case ATTRIBUTES_UPDATED: |
||||
|
try { |
||||
|
JsonObject data = entityData.getAsJsonObject(); |
||||
|
TransportProtos.PostAttributeMsg attributesUpdatedMsg = JsonConverter.convertToAttributesProto(data.getAsJsonObject("kv")); |
||||
|
builder.setAttributesUpdatedMsg(attributesUpdatedMsg); |
||||
|
builder.setPostAttributeScope(data.getAsJsonPrimitive("scope").getAsString()); |
||||
|
} catch (Exception e) { |
||||
|
log.warn("[{}] Can't convert to AttributesUpdatedMsg proto, entityData [{}]", entityId, entityData, e); |
||||
|
} |
||||
|
break; |
||||
|
case POST_ATTRIBUTES: |
||||
|
try { |
||||
|
JsonObject data = entityData.getAsJsonObject(); |
||||
|
TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(data.getAsJsonObject("kv")); |
||||
|
builder.setPostAttributesMsg(postAttributesMsg); |
||||
|
builder.setPostAttributeScope(data.getAsJsonPrimitive("scope").getAsString()); |
||||
|
} catch (Exception e) { |
||||
|
log.warn("[{}] Can't convert to PostAttributesMsg, entityData [{}]", entityId, entityData, e); |
||||
|
} |
||||
|
break; |
||||
|
case ATTRIBUTES_DELETED: |
||||
|
try { |
||||
|
AttributeDeleteMsg.Builder attributeDeleteMsg = AttributeDeleteMsg.newBuilder(); |
||||
|
attributeDeleteMsg.setScope(entityData.getAsJsonObject().getAsJsonPrimitive("scope").getAsString()); |
||||
|
JsonArray jsonArray = entityData.getAsJsonObject().getAsJsonArray("keys"); |
||||
|
List<String> keys = new Gson().fromJson(jsonArray.toString(), List.class); |
||||
|
attributeDeleteMsg.addAllAttributeNames(keys); |
||||
|
attributeDeleteMsg.build(); |
||||
|
builder.setAttributeDeleteMsg(attributeDeleteMsg); |
||||
|
} catch (Exception e) { |
||||
|
log.warn("[{}] Can't convert to AttributeDeleteMsg proto, entityData [{}]", entityId, entityData, e); |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
return builder.build(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,69 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.constructor; |
||||
|
|
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.EntityView; |
||||
|
import org.thingsboard.server.common.data.id.CustomerId; |
||||
|
import org.thingsboard.server.common.data.id.EntityViewId; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.gen.edge.EdgeEntityType; |
||||
|
import org.thingsboard.server.gen.edge.EntityViewUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.UpdateMsgType; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
@Component |
||||
|
@TbCoreComponent |
||||
|
public class EntityViewMsgConstructor { |
||||
|
|
||||
|
public EntityViewUpdateMsg constructEntityViewUpdatedMsg(UpdateMsgType msgType, EntityView entityView, CustomerId customerId) { |
||||
|
EdgeEntityType entityType; |
||||
|
switch (entityView.getEntityId().getEntityType()) { |
||||
|
case DEVICE: |
||||
|
entityType = EdgeEntityType.DEVICE; |
||||
|
break; |
||||
|
case ASSET: |
||||
|
entityType = EdgeEntityType.ASSET; |
||||
|
break; |
||||
|
default: |
||||
|
throw new RuntimeException("Unsupported entity type [" + entityView.getEntityId().getEntityType() + "]"); |
||||
|
} |
||||
|
EntityViewUpdateMsg.Builder builder = EntityViewUpdateMsg.newBuilder() |
||||
|
.setMsgType(msgType) |
||||
|
.setIdMSB(entityView.getId().getId().getMostSignificantBits()) |
||||
|
.setIdLSB(entityView.getId().getId().getLeastSignificantBits()) |
||||
|
.setName(entityView.getName()) |
||||
|
.setType(entityView.getType()) |
||||
|
.setEntityIdMSB(entityView.getEntityId().getId().getMostSignificantBits()) |
||||
|
.setEntityIdLSB(entityView.getEntityId().getId().getLeastSignificantBits()) |
||||
|
.setEntityType(entityType); |
||||
|
if (customerId != null) { |
||||
|
builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits()); |
||||
|
builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits()); |
||||
|
} |
||||
|
if (entityView.getAdditionalInfo() != null) { |
||||
|
builder.setAdditionalInfo(JacksonUtil.toString(entityView.getAdditionalInfo())); |
||||
|
} |
||||
|
return builder.build(); |
||||
|
} |
||||
|
|
||||
|
public EntityViewUpdateMsg constructEntityViewDeleteMsg(EntityViewId entityViewId) { |
||||
|
return EntityViewUpdateMsg.newBuilder() |
||||
|
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) |
||||
|
.setIdMSB(entityViewId.getId().getMostSignificantBits()) |
||||
|
.setIdLSB(entityViewId.getId().getLeastSignificantBits()).build(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.constructor; |
||||
|
|
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.relation.EntityRelation; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.gen.edge.RelationUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.UpdateMsgType; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
@Component |
||||
|
@TbCoreComponent |
||||
|
public class RelationMsgConstructor { |
||||
|
|
||||
|
public RelationUpdateMsg constructRelationUpdatedMsg(UpdateMsgType msgType, EntityRelation entityRelation) { |
||||
|
RelationUpdateMsg.Builder builder = RelationUpdateMsg.newBuilder() |
||||
|
.setMsgType(msgType) |
||||
|
.setFromIdMSB(entityRelation.getFrom().getId().getMostSignificantBits()) |
||||
|
.setFromIdLSB(entityRelation.getFrom().getId().getLeastSignificantBits()) |
||||
|
.setFromEntityType(entityRelation.getFrom().getEntityType().name()) |
||||
|
.setToIdMSB(entityRelation.getTo().getId().getMostSignificantBits()) |
||||
|
.setToIdLSB(entityRelation.getTo().getId().getLeastSignificantBits()) |
||||
|
.setToEntityType(entityRelation.getTo().getEntityType().name()) |
||||
|
.setType(entityRelation.getType()) |
||||
|
.setAdditionalInfo(JacksonUtil.toString(entityRelation.getAdditionalInfo())); |
||||
|
if (entityRelation.getTypeGroup() != null) { |
||||
|
builder.setTypeGroup(entityRelation.getTypeGroup().name()); |
||||
|
} |
||||
|
return builder.build(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,151 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.constructor; |
||||
|
|
||||
|
import com.fasterxml.jackson.core.JsonProcessingException; |
||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.id.RuleChainId; |
||||
|
import org.thingsboard.server.common.data.rule.NodeConnectionInfo; |
||||
|
import org.thingsboard.server.common.data.rule.RuleChain; |
||||
|
import org.thingsboard.server.common.data.rule.RuleChainConnectionInfo; |
||||
|
import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
||||
|
import org.thingsboard.server.common.data.rule.RuleNode; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.gen.edge.NodeConnectionInfoProto; |
||||
|
import org.thingsboard.server.gen.edge.RuleChainConnectionInfoProto; |
||||
|
import org.thingsboard.server.gen.edge.RuleChainMetadataUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.RuleChainUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.RuleNodeProto; |
||||
|
import org.thingsboard.server.gen.edge.UpdateMsgType; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
@Component |
||||
|
@Slf4j |
||||
|
@TbCoreComponent |
||||
|
public class RuleChainMsgConstructor { |
||||
|
|
||||
|
private static final ObjectMapper objectMapper = new ObjectMapper(); |
||||
|
|
||||
|
public RuleChainUpdateMsg constructRuleChainUpdatedMsg(RuleChainId edgeRootRuleChainId, UpdateMsgType msgType, RuleChain ruleChain) { |
||||
|
RuleChainUpdateMsg.Builder builder = RuleChainUpdateMsg.newBuilder() |
||||
|
.setMsgType(msgType) |
||||
|
.setIdMSB(ruleChain.getId().getId().getMostSignificantBits()) |
||||
|
.setIdLSB(ruleChain.getId().getId().getLeastSignificantBits()) |
||||
|
.setName(ruleChain.getName()) |
||||
|
.setRoot(ruleChain.getId().equals(edgeRootRuleChainId)) |
||||
|
.setDebugMode(ruleChain.isDebugMode()) |
||||
|
.setConfiguration(JacksonUtil.toString(ruleChain.getConfiguration())); |
||||
|
if (ruleChain.getFirstRuleNodeId() != null) { |
||||
|
builder.setFirstRuleNodeIdMSB(ruleChain.getFirstRuleNodeId().getId().getMostSignificantBits()) |
||||
|
.setFirstRuleNodeIdLSB(ruleChain.getFirstRuleNodeId().getId().getLeastSignificantBits()); |
||||
|
} |
||||
|
return builder.build(); |
||||
|
} |
||||
|
|
||||
|
public RuleChainMetadataUpdateMsg constructRuleChainMetadataUpdatedMsg(UpdateMsgType msgType, RuleChainMetaData ruleChainMetaData) { |
||||
|
try { |
||||
|
RuleChainMetadataUpdateMsg.Builder builder = RuleChainMetadataUpdateMsg.newBuilder() |
||||
|
.setRuleChainIdMSB(ruleChainMetaData.getRuleChainId().getId().getMostSignificantBits()) |
||||
|
.setRuleChainIdLSB(ruleChainMetaData.getRuleChainId().getId().getLeastSignificantBits()) |
||||
|
.addAllNodes(constructNodes(ruleChainMetaData.getNodes())) |
||||
|
.addAllConnections(constructConnections(ruleChainMetaData.getConnections())) |
||||
|
.addAllRuleChainConnections(constructRuleChainConnections(ruleChainMetaData.getRuleChainConnections())); |
||||
|
if (ruleChainMetaData.getFirstNodeIndex() != null) { |
||||
|
builder.setFirstNodeIndex(ruleChainMetaData.getFirstNodeIndex()); |
||||
|
} else { |
||||
|
builder.setFirstNodeIndex(-1); |
||||
|
} |
||||
|
builder.setMsgType(msgType); |
||||
|
return builder.build(); |
||||
|
} catch (JsonProcessingException ex) { |
||||
|
log.error("Can't construct RuleChainMetadataUpdateMsg", ex); |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
|
||||
|
private List<NodeConnectionInfoProto> constructConnections(List<NodeConnectionInfo> connections) { |
||||
|
List<NodeConnectionInfoProto> result = new ArrayList<>(); |
||||
|
if (connections != null && !connections.isEmpty()) { |
||||
|
for (NodeConnectionInfo connection : connections) { |
||||
|
result.add(constructConnection(connection)); |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private NodeConnectionInfoProto constructConnection(NodeConnectionInfo connection) { |
||||
|
return NodeConnectionInfoProto.newBuilder() |
||||
|
.setFromIndex(connection.getFromIndex()) |
||||
|
.setToIndex(connection.getToIndex()) |
||||
|
.setType(connection.getType()) |
||||
|
.build(); |
||||
|
} |
||||
|
|
||||
|
private List<RuleNodeProto> constructNodes(List<RuleNode> nodes) throws JsonProcessingException { |
||||
|
List<RuleNodeProto> result = new ArrayList<>(); |
||||
|
if (nodes != null && !nodes.isEmpty()) { |
||||
|
for (RuleNode node : nodes) { |
||||
|
result.add(constructNode(node)); |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private List<RuleChainConnectionInfoProto> constructRuleChainConnections(List<RuleChainConnectionInfo> ruleChainConnections) throws JsonProcessingException { |
||||
|
List<RuleChainConnectionInfoProto> result = new ArrayList<>(); |
||||
|
if (ruleChainConnections != null && !ruleChainConnections.isEmpty()) { |
||||
|
for (RuleChainConnectionInfo ruleChainConnectionInfo : ruleChainConnections) { |
||||
|
result.add(constructRuleChainConnection(ruleChainConnectionInfo)); |
||||
|
} |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private RuleChainConnectionInfoProto constructRuleChainConnection(RuleChainConnectionInfo ruleChainConnectionInfo) throws JsonProcessingException { |
||||
|
return RuleChainConnectionInfoProto.newBuilder() |
||||
|
.setFromIndex(ruleChainConnectionInfo.getFromIndex()) |
||||
|
.setTargetRuleChainIdMSB(ruleChainConnectionInfo.getTargetRuleChainId().getId().getMostSignificantBits()) |
||||
|
.setTargetRuleChainIdLSB(ruleChainConnectionInfo.getTargetRuleChainId().getId().getLeastSignificantBits()) |
||||
|
.setType(ruleChainConnectionInfo.getType()) |
||||
|
.setAdditionalInfo(objectMapper.writeValueAsString(ruleChainConnectionInfo.getAdditionalInfo())) |
||||
|
.build(); |
||||
|
} |
||||
|
|
||||
|
private RuleNodeProto constructNode(RuleNode node) throws JsonProcessingException { |
||||
|
return RuleNodeProto.newBuilder() |
||||
|
.setIdMSB(node.getId().getId().getMostSignificantBits()) |
||||
|
.setIdLSB(node.getId().getId().getLeastSignificantBits()) |
||||
|
.setType(node.getType()) |
||||
|
.setName(node.getName()) |
||||
|
.setDebugMode(node.isDebugMode()) |
||||
|
.setConfiguration(objectMapper.writeValueAsString(node.getConfiguration())) |
||||
|
.setAdditionalInfo(objectMapper.writeValueAsString(node.getAdditionalInfo())) |
||||
|
.build(); |
||||
|
} |
||||
|
|
||||
|
public RuleChainUpdateMsg constructRuleChainDeleteMsg(RuleChainId ruleChainId) { |
||||
|
return RuleChainUpdateMsg.newBuilder() |
||||
|
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) |
||||
|
.setIdMSB(ruleChainId.getId().getMostSignificantBits()) |
||||
|
.setIdLSB(ruleChainId.getId().getLeastSignificantBits()).build(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,74 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.constructor; |
||||
|
|
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.User; |
||||
|
import org.thingsboard.server.common.data.id.CustomerId; |
||||
|
import org.thingsboard.server.common.data.id.UserId; |
||||
|
import org.thingsboard.server.common.data.security.UserCredentials; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.gen.edge.UpdateMsgType; |
||||
|
import org.thingsboard.server.gen.edge.UserCredentialsUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.UserUpdateMsg; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
@Component |
||||
|
@TbCoreComponent |
||||
|
public class UserMsgConstructor { |
||||
|
|
||||
|
public UserUpdateMsg constructUserUpdatedMsg(UpdateMsgType msgType, User user, CustomerId customerId) { |
||||
|
UserUpdateMsg.Builder builder = UserUpdateMsg.newBuilder() |
||||
|
.setMsgType(msgType) |
||||
|
.setIdMSB(user.getId().getId().getMostSignificantBits()) |
||||
|
.setIdLSB(user.getId().getId().getLeastSignificantBits()) |
||||
|
.setEmail(user.getEmail()) |
||||
|
.setAuthority(user.getAuthority().name()); |
||||
|
if (customerId != null) { |
||||
|
builder.setCustomerIdMSB(customerId.getId().getMostSignificantBits()); |
||||
|
builder.setCustomerIdLSB(customerId.getId().getLeastSignificantBits()); |
||||
|
} |
||||
|
if (user.getFirstName() != null) { |
||||
|
builder.setFirstName(user.getFirstName()); |
||||
|
} |
||||
|
if (user.getLastName() != null) { |
||||
|
builder.setLastName(user.getLastName()); |
||||
|
} |
||||
|
if (user.getAdditionalInfo() != null) { |
||||
|
builder.setAdditionalInfo(JacksonUtil.toString(user.getAdditionalInfo())); |
||||
|
} |
||||
|
if (user.getAdditionalInfo() != null) { |
||||
|
builder.setAdditionalInfo(JacksonUtil.toString(user.getAdditionalInfo())); |
||||
|
} |
||||
|
return builder.build(); |
||||
|
} |
||||
|
|
||||
|
public UserUpdateMsg constructUserDeleteMsg(UserId userId) { |
||||
|
return UserUpdateMsg.newBuilder() |
||||
|
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) |
||||
|
.setIdMSB(userId.getId().getMostSignificantBits()) |
||||
|
.setIdLSB(userId.getId().getLeastSignificantBits()).build(); |
||||
|
} |
||||
|
|
||||
|
public UserCredentialsUpdateMsg constructUserCredentialsUpdatedMsg(UserCredentials userCredentials) { |
||||
|
UserCredentialsUpdateMsg.Builder builder = UserCredentialsUpdateMsg.newBuilder() |
||||
|
.setUserIdMSB(userCredentials.getUserId().getId().getMostSignificantBits()) |
||||
|
.setUserIdLSB(userCredentials.getUserId().getId().getLeastSignificantBits()) |
||||
|
.setEnabled(userCredentials.isEnabled()) |
||||
|
.setPassword(userCredentials.getPassword()); |
||||
|
return builder.build(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,61 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.constructor; |
||||
|
|
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.id.WidgetTypeId; |
||||
|
import org.thingsboard.server.common.data.widget.WidgetType; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.gen.edge.UpdateMsgType; |
||||
|
import org.thingsboard.server.gen.edge.WidgetTypeUpdateMsg; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
@Component |
||||
|
@TbCoreComponent |
||||
|
public class WidgetTypeMsgConstructor { |
||||
|
|
||||
|
public WidgetTypeUpdateMsg constructWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetType widgetType) { |
||||
|
WidgetTypeUpdateMsg.Builder builder = WidgetTypeUpdateMsg.newBuilder() |
||||
|
.setMsgType(msgType) |
||||
|
.setIdMSB(widgetType.getId().getId().getMostSignificantBits()) |
||||
|
.setIdLSB(widgetType.getId().getId().getLeastSignificantBits()); |
||||
|
if (widgetType.getBundleAlias() != null) { |
||||
|
builder.setBundleAlias(widgetType.getBundleAlias()); |
||||
|
} |
||||
|
if (widgetType.getAlias() != null) { |
||||
|
builder.setAlias(widgetType.getAlias()); |
||||
|
} |
||||
|
if (widgetType.getName() != null) { |
||||
|
builder.setName(widgetType.getName()); |
||||
|
} |
||||
|
if (widgetType.getDescriptor() != null) { |
||||
|
builder.setDescriptorJson(JacksonUtil.toString(widgetType.getDescriptor())); |
||||
|
} |
||||
|
if (widgetType.getTenantId().equals(TenantId.SYS_TENANT_ID)) { |
||||
|
builder.setIsSystem(true); |
||||
|
} |
||||
|
return builder.build(); |
||||
|
} |
||||
|
|
||||
|
public WidgetTypeUpdateMsg constructWidgetTypeDeleteMsg(WidgetTypeId widgetTypeId) { |
||||
|
return WidgetTypeUpdateMsg.newBuilder() |
||||
|
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) |
||||
|
.setIdMSB(widgetTypeId.getId().getMostSignificantBits()) |
||||
|
.setIdLSB(widgetTypeId.getId().getLeastSignificantBits()) |
||||
|
.build(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,56 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.constructor; |
||||
|
|
||||
|
import com.google.protobuf.ByteString; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.id.WidgetsBundleId; |
||||
|
import org.thingsboard.server.common.data.widget.WidgetsBundle; |
||||
|
import org.thingsboard.server.gen.edge.UpdateMsgType; |
||||
|
import org.thingsboard.server.gen.edge.WidgetsBundleUpdateMsg; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
import java.nio.charset.StandardCharsets; |
||||
|
|
||||
|
@Component |
||||
|
@TbCoreComponent |
||||
|
public class WidgetsBundleMsgConstructor { |
||||
|
|
||||
|
public WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle) { |
||||
|
WidgetsBundleUpdateMsg.Builder builder = WidgetsBundleUpdateMsg.newBuilder() |
||||
|
.setMsgType(msgType) |
||||
|
.setIdMSB(widgetsBundle.getId().getId().getMostSignificantBits()) |
||||
|
.setIdLSB(widgetsBundle.getId().getId().getLeastSignificantBits()) |
||||
|
.setTitle(widgetsBundle.getTitle()) |
||||
|
.setAlias(widgetsBundle.getAlias()); |
||||
|
if (widgetsBundle.getImage() != null) { |
||||
|
builder.setImage(ByteString.copyFrom(widgetsBundle.getImage().getBytes(StandardCharsets.UTF_8))); |
||||
|
} |
||||
|
if (widgetsBundle.getTenantId().equals(TenantId.SYS_TENANT_ID)) { |
||||
|
builder.setIsSystem(true); |
||||
|
} |
||||
|
return builder.build(); |
||||
|
} |
||||
|
|
||||
|
public WidgetsBundleUpdateMsg constructWidgetsBundleDeleteMsg(WidgetsBundleId widgetsBundleId) { |
||||
|
return WidgetsBundleUpdateMsg.newBuilder() |
||||
|
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) |
||||
|
.setIdMSB(widgetsBundleId.getId().getMostSignificantBits()) |
||||
|
.setIdLSB(widgetsBundleId.getId().getLeastSignificantBits()) |
||||
|
.build(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,671 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.init; |
||||
|
|
||||
|
import com.datastax.oss.driver.api.core.uuid.Uuids; |
||||
|
import com.fasterxml.jackson.databind.JsonNode; |
||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
|
import com.fasterxml.jackson.databind.node.ObjectNode; |
||||
|
import com.google.common.util.concurrent.FutureCallback; |
||||
|
import com.google.common.util.concurrent.Futures; |
||||
|
import com.google.common.util.concurrent.ListenableFuture; |
||||
|
import com.google.common.util.concurrent.SettableFuture; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.io.FileUtils; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
import org.apache.commons.lang3.text.WordUtils; |
||||
|
import org.checkerframework.checker.nullness.qual.Nullable; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.core.io.DefaultResourceLoader; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.server.common.data.AdminSettings; |
||||
|
import org.thingsboard.server.common.data.DashboardInfo; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.DeviceProfile; |
||||
|
import org.thingsboard.server.common.data.EdgeUtils; |
||||
|
import org.thingsboard.server.common.data.EntityType; |
||||
|
import org.thingsboard.server.common.data.EntityView; |
||||
|
import org.thingsboard.server.common.data.User; |
||||
|
import org.thingsboard.server.common.data.asset.Asset; |
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEvent; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventType; |
||||
|
import org.thingsboard.server.common.data.id.AdminSettingsId; |
||||
|
import org.thingsboard.server.common.data.id.DeviceId; |
||||
|
import org.thingsboard.server.common.data.id.EdgeId; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
import org.thingsboard.server.common.data.id.EntityIdFactory; |
||||
|
import org.thingsboard.server.common.data.id.RuleChainId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.id.UserId; |
||||
|
import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
||||
|
import org.thingsboard.server.common.data.kv.DataType; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.common.data.relation.EntityRelation; |
||||
|
import org.thingsboard.server.common.data.relation.EntityRelationsQuery; |
||||
|
import org.thingsboard.server.common.data.relation.EntitySearchDirection; |
||||
|
import org.thingsboard.server.common.data.relation.RelationsSearchParameters; |
||||
|
import org.thingsboard.server.common.data.rule.RuleChain; |
||||
|
import org.thingsboard.server.common.data.widget.WidgetType; |
||||
|
import org.thingsboard.server.common.data.widget.WidgetsBundle; |
||||
|
import org.thingsboard.server.dao.asset.AssetService; |
||||
|
import org.thingsboard.server.dao.attributes.AttributesService; |
||||
|
import org.thingsboard.server.dao.dashboard.DashboardService; |
||||
|
import org.thingsboard.server.dao.device.DeviceProfileService; |
||||
|
import org.thingsboard.server.dao.device.DeviceService; |
||||
|
import org.thingsboard.server.dao.edge.EdgeEventService; |
||||
|
import org.thingsboard.server.dao.entityview.EntityViewService; |
||||
|
import org.thingsboard.server.dao.relation.RelationService; |
||||
|
import org.thingsboard.server.dao.rule.RuleChainService; |
||||
|
import org.thingsboard.server.dao.settings.AdminSettingsService; |
||||
|
import org.thingsboard.server.dao.user.UserService; |
||||
|
import org.thingsboard.server.dao.widget.WidgetTypeService; |
||||
|
import org.thingsboard.server.dao.widget.WidgetsBundleService; |
||||
|
import org.thingsboard.server.gen.edge.AttributesRequestMsg; |
||||
|
import org.thingsboard.server.gen.edge.DeviceCredentialsRequestMsg; |
||||
|
import org.thingsboard.server.gen.edge.RelationRequestMsg; |
||||
|
import org.thingsboard.server.gen.edge.RuleChainMetadataRequestMsg; |
||||
|
import org.thingsboard.server.gen.edge.UserCredentialsRequestMsg; |
||||
|
import org.thingsboard.server.service.executors.DbCallbackExecutorService; |
||||
|
import org.thingsboard.server.service.queue.TbClusterService; |
||||
|
|
||||
|
import java.io.File; |
||||
|
import java.nio.charset.StandardCharsets; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.HashMap; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
import java.util.UUID; |
||||
|
import java.util.regex.Matcher; |
||||
|
import java.util.regex.Pattern; |
||||
|
|
||||
|
@Service |
||||
|
@Slf4j |
||||
|
public class DefaultSyncEdgeService implements SyncEdgeService { |
||||
|
|
||||
|
private static final ObjectMapper mapper = new ObjectMapper(); |
||||
|
|
||||
|
private static final int DEFAULT_LIMIT = 100; |
||||
|
|
||||
|
@Autowired |
||||
|
private EdgeEventService edgeEventService; |
||||
|
|
||||
|
@Autowired |
||||
|
private AttributesService attributesService; |
||||
|
|
||||
|
@Autowired |
||||
|
private RuleChainService ruleChainService; |
||||
|
|
||||
|
@Autowired |
||||
|
private RelationService relationService; |
||||
|
|
||||
|
@Autowired |
||||
|
private DeviceService deviceService; |
||||
|
|
||||
|
@Autowired |
||||
|
private DeviceProfileService deviceProfileService; |
||||
|
|
||||
|
@Autowired |
||||
|
private AssetService assetService; |
||||
|
|
||||
|
@Autowired |
||||
|
private EntityViewService entityViewService; |
||||
|
|
||||
|
@Autowired |
||||
|
private DashboardService dashboardService; |
||||
|
|
||||
|
@Autowired |
||||
|
private UserService userService; |
||||
|
|
||||
|
@Autowired |
||||
|
private WidgetsBundleService widgetsBundleService; |
||||
|
|
||||
|
@Autowired |
||||
|
private WidgetTypeService widgetTypeService; |
||||
|
|
||||
|
@Autowired |
||||
|
private AdminSettingsService adminSettingsService; |
||||
|
|
||||
|
@Autowired |
||||
|
private DbCallbackExecutorService dbCallbackExecutorService; |
||||
|
|
||||
|
@Autowired |
||||
|
private TbClusterService tbClusterService; |
||||
|
|
||||
|
@Override |
||||
|
public void sync(TenantId tenantId, Edge edge) { |
||||
|
log.trace("[{}][{}] Staring edge sync process", tenantId, edge.getId()); |
||||
|
try { |
||||
|
syncWidgetsBundles(tenantId, edge); |
||||
|
// TODO: voba - implement this functionality
|
||||
|
// syncAdminSettings(edge);
|
||||
|
syncDeviceProfiles(tenantId, edge); |
||||
|
syncRuleChains(tenantId, edge); |
||||
|
syncUsers(tenantId, edge); |
||||
|
syncAssets(tenantId, edge); |
||||
|
syncEntityViews(tenantId, edge); |
||||
|
syncDashboards(tenantId, edge); |
||||
|
syncWidgetsTypes(tenantId, edge); |
||||
|
syncDevices(tenantId, edge); |
||||
|
} catch (Exception e) { |
||||
|
log.error("[{}][{}] Exception during sync process", tenantId, edge.getId(), e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void syncRuleChains(TenantId tenantId, Edge edge) { |
||||
|
log.trace("[{}] syncRuleChains [{}]", tenantId, edge.getName()); |
||||
|
try { |
||||
|
PageLink pageLink = new PageLink(DEFAULT_LIMIT); |
||||
|
PageData<RuleChain> pageData; |
||||
|
do { |
||||
|
pageData = ruleChainService.findRuleChainsByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink); |
||||
|
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { |
||||
|
log.trace("[{}] [{}] rule chains(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); |
||||
|
for (RuleChain ruleChain : pageData.getData()) { |
||||
|
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN, EdgeEventActionType.ADDED, ruleChain.getId(), null); |
||||
|
} |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} |
||||
|
} while (pageData != null && pageData.hasNext()); |
||||
|
} catch (Exception e) { |
||||
|
log.error("Exception during loading edge rule chain(s) on sync!", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void syncDevices(TenantId tenantId, Edge edge) { |
||||
|
log.trace("[{}] syncDevices [{}]", tenantId, edge.getName()); |
||||
|
try { |
||||
|
PageLink pageLink = new PageLink(DEFAULT_LIMIT); |
||||
|
PageData<Device> pageData; |
||||
|
do { |
||||
|
pageData = deviceService.findDevicesByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink); |
||||
|
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { |
||||
|
log.trace("[{}] [{}] device(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); |
||||
|
for (Device device : pageData.getData()) { |
||||
|
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ADDED, device.getId(), null); |
||||
|
} |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} |
||||
|
} while (pageData != null && pageData.hasNext()); |
||||
|
} catch (Exception e) { |
||||
|
log.error("Exception during loading edge device(s) on sync!", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void syncDeviceProfiles(TenantId tenantId, Edge edge) { |
||||
|
log.trace("[{}] syncDeviceProfiles [{}]", tenantId, edge.getName()); |
||||
|
try { |
||||
|
PageLink pageLink = new PageLink(DEFAULT_LIMIT); |
||||
|
PageData<DeviceProfile> pageData; |
||||
|
do { |
||||
|
pageData = deviceProfileService.findDeviceProfiles(tenantId, pageLink); |
||||
|
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { |
||||
|
log.trace("[{}] [{}] user(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); |
||||
|
for (DeviceProfile deviceProfile : pageData.getData()) { |
||||
|
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE_PROFILE, EdgeEventActionType.ADDED, deviceProfile.getId(), null); |
||||
|
} |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} |
||||
|
} while (pageData != null && pageData.hasNext()); |
||||
|
} catch (Exception e) { |
||||
|
log.error("Exception during loading device profile(s) on sync!", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void syncAssets(TenantId tenantId, Edge edge) { |
||||
|
log.trace("[{}] syncAssets [{}]", tenantId, edge.getName()); |
||||
|
try { |
||||
|
PageLink pageLink = new PageLink(DEFAULT_LIMIT); |
||||
|
PageData<Asset> pageData; |
||||
|
do { |
||||
|
pageData = assetService.findAssetsByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink); |
||||
|
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { |
||||
|
log.trace("[{}] [{}] asset(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); |
||||
|
for (Asset asset : pageData.getData()) { |
||||
|
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ASSET, EdgeEventActionType.ADDED, asset.getId(), null); |
||||
|
} |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} |
||||
|
} while (pageData != null && pageData.hasNext()); |
||||
|
} catch (Exception e) { |
||||
|
log.error("Exception during loading edge asset(s) on sync!", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void syncEntityViews(TenantId tenantId, Edge edge) { |
||||
|
log.trace("[{}] syncEntityViews [{}]", tenantId, edge.getName()); |
||||
|
try { |
||||
|
PageLink pageLink = new PageLink(DEFAULT_LIMIT); |
||||
|
PageData<EntityView> pageData; |
||||
|
do { |
||||
|
pageData = entityViewService.findEntityViewsByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink); |
||||
|
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { |
||||
|
log.trace("[{}] [{}] entity view(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); |
||||
|
for (EntityView entityView : pageData.getData()) { |
||||
|
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ENTITY_VIEW, EdgeEventActionType.ADDED, entityView.getId(), null); |
||||
|
} |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} |
||||
|
} while (pageData != null && pageData.hasNext()); |
||||
|
} catch (Exception e) { |
||||
|
log.error("Exception during loading edge entity view(s) on sync!", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void syncDashboards(TenantId tenantId, Edge edge) { |
||||
|
log.trace("[{}] syncDashboards [{}]", tenantId, edge.getName()); |
||||
|
try { |
||||
|
PageLink pageLink = new PageLink(DEFAULT_LIMIT); |
||||
|
PageData<DashboardInfo> pageData; |
||||
|
do { |
||||
|
pageData = dashboardService.findDashboardsByTenantIdAndEdgeId(tenantId, edge.getId(), pageLink); |
||||
|
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { |
||||
|
log.trace("[{}] [{}] dashboard(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); |
||||
|
for (DashboardInfo dashboardInfo : pageData.getData()) { |
||||
|
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DASHBOARD, EdgeEventActionType.ADDED, dashboardInfo.getId(), null); |
||||
|
} |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} |
||||
|
} while (pageData != null && pageData.hasNext()); |
||||
|
} catch (Exception e) { |
||||
|
log.error("Exception during loading edge dashboard(s) on sync!", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void syncUsers(TenantId tenantId, Edge edge) { |
||||
|
log.trace("[{}] syncUsers [{}]", tenantId, edge.getName()); |
||||
|
try { |
||||
|
PageLink pageLink = new PageLink(DEFAULT_LIMIT); |
||||
|
PageData<User> pageData; |
||||
|
do { |
||||
|
pageData = userService.findTenantAdmins(tenantId, pageLink); |
||||
|
pushUsersToEdge(tenantId, pageData, edge); |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} while (pageData.hasNext()); |
||||
|
syncCustomerUsers(tenantId, edge); |
||||
|
} catch (Exception e) { |
||||
|
log.error("Exception during loading edge user(s) on sync!", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void syncCustomerUsers(TenantId tenantId, Edge edge) { |
||||
|
if (edge.getCustomerId() != null && !EntityId.NULL_UUID.equals(edge.getCustomerId().getId())) { |
||||
|
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.CUSTOMER, EdgeEventActionType.ADDED, edge.getCustomerId(), null); |
||||
|
PageLink pageLink = new PageLink(DEFAULT_LIMIT); |
||||
|
PageData<User> pageData; |
||||
|
do { |
||||
|
pageData = userService.findCustomerUsers(tenantId, edge.getCustomerId(), pageLink); |
||||
|
pushUsersToEdge(tenantId, pageData, edge); |
||||
|
if (pageData != null && pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} while (pageData != null && pageData.hasNext()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void pushUsersToEdge(TenantId tenantId, PageData<User> pageData, Edge edge) { |
||||
|
if (pageData != null && pageData.getData() != null && !pageData.getData().isEmpty()) { |
||||
|
log.trace("[{}] [{}] user(s) are going to be pushed to edge.", edge.getId(), pageData.getData().size()); |
||||
|
for (User user : pageData.getData()) { |
||||
|
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.USER, EdgeEventActionType.ADDED, user.getId(), null); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void syncWidgetsBundles(TenantId tenantId, Edge edge) { |
||||
|
log.trace("[{}] syncWidgetsBundles [{}]", tenantId, edge.getName()); |
||||
|
List<WidgetsBundle> widgetsBundlesToPush = new ArrayList<>(); |
||||
|
widgetsBundlesToPush.addAll(widgetsBundleService.findAllTenantWidgetsBundlesByTenantId(tenantId)); |
||||
|
widgetsBundlesToPush.addAll(widgetsBundleService.findSystemWidgetsBundles(tenantId)); |
||||
|
try { |
||||
|
for (WidgetsBundle widgetsBundle : widgetsBundlesToPush) { |
||||
|
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGETS_BUNDLE, EdgeEventActionType.ADDED, widgetsBundle.getId(), null); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
log.error("Exception during loading widgets bundle(s) on sync!", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void syncWidgetsTypes(TenantId tenantId, Edge edge) { |
||||
|
log.trace("[{}] syncWidgetsTypes [{}]", tenantId, edge.getName()); |
||||
|
List<WidgetsBundle> widgetsBundlesToPush = new ArrayList<>(); |
||||
|
widgetsBundlesToPush.addAll(widgetsBundleService.findAllTenantWidgetsBundlesByTenantId(tenantId)); |
||||
|
widgetsBundlesToPush.addAll(widgetsBundleService.findSystemWidgetsBundles(tenantId)); |
||||
|
try { |
||||
|
for (WidgetsBundle widgetsBundle : widgetsBundlesToPush) { |
||||
|
List<WidgetType> widgetTypesToPush = |
||||
|
widgetTypeService.findWidgetTypesByTenantIdAndBundleAlias(widgetsBundle.getTenantId(), widgetsBundle.getAlias()); |
||||
|
for (WidgetType widgetType : widgetTypesToPush) { |
||||
|
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.WIDGET_TYPE, EdgeEventActionType.ADDED, widgetType.getId(), null); |
||||
|
} |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
log.error("Exception during loading widgets type(s) on sync!", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void syncAdminSettings(TenantId tenantId, Edge edge) { |
||||
|
log.trace("[{}] syncAdminSettings [{}]", tenantId, edge.getName()); |
||||
|
try { |
||||
|
AdminSettings systemMailSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail"); |
||||
|
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, EdgeEventActionType.UPDATED, null, mapper.valueToTree(systemMailSettings)); |
||||
|
AdminSettings tenantMailSettings = convertToTenantAdminSettings(systemMailSettings.getKey(), (ObjectNode) systemMailSettings.getJsonValue()); |
||||
|
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, EdgeEventActionType.UPDATED, null, mapper.valueToTree(tenantMailSettings)); |
||||
|
AdminSettings systemMailTemplates = loadMailTemplates(); |
||||
|
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, EdgeEventActionType.UPDATED, null, mapper.valueToTree(systemMailTemplates)); |
||||
|
AdminSettings tenantMailTemplates = convertToTenantAdminSettings(systemMailTemplates.getKey(), (ObjectNode) systemMailTemplates.getJsonValue()); |
||||
|
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.ADMIN_SETTINGS, EdgeEventActionType.UPDATED, null, mapper.valueToTree(tenantMailTemplates)); |
||||
|
} catch (Exception e) { |
||||
|
log.error("Can't load admin settings", e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private AdminSettings loadMailTemplates() throws Exception { |
||||
|
Map<String, Object> mailTemplates = new HashMap<>(); |
||||
|
Pattern startPattern = Pattern.compile("<div class=\"content\".*?>"); |
||||
|
Pattern endPattern = Pattern.compile("<div class=\"footer\".*?>"); |
||||
|
File[] files = new DefaultResourceLoader().getResource("classpath:/templates/").getFile().listFiles(); |
||||
|
for (File file : files) { |
||||
|
Map<String, String> mailTemplate = new HashMap<>(); |
||||
|
String name = validateName(file.getName()); |
||||
|
String stringTemplate = FileUtils.readFileToString(file, StandardCharsets.UTF_8); |
||||
|
Matcher start = startPattern.matcher(stringTemplate); |
||||
|
Matcher end = endPattern.matcher(stringTemplate); |
||||
|
if (start.find() && end.find()) { |
||||
|
String body = StringUtils.substringBetween(stringTemplate, start.group(), end.group()).replaceAll("\t", ""); |
||||
|
String subject = StringUtils.substringBetween(body, "<h2>", "</h2>"); |
||||
|
mailTemplate.put("subject", subject); |
||||
|
mailTemplate.put("body", body); |
||||
|
mailTemplates.put(name, mailTemplate); |
||||
|
} else { |
||||
|
log.error("Can't load mail template from file {}", file.getName()); |
||||
|
} |
||||
|
} |
||||
|
AdminSettings adminSettings = new AdminSettings(); |
||||
|
adminSettings.setId(new AdminSettingsId(Uuids.timeBased())); |
||||
|
adminSettings.setKey("mailTemplates"); |
||||
|
adminSettings.setJsonValue(mapper.convertValue(mailTemplates, JsonNode.class)); |
||||
|
return adminSettings; |
||||
|
} |
||||
|
|
||||
|
private String validateName(String name) throws Exception { |
||||
|
StringBuilder nameBuilder = new StringBuilder(); |
||||
|
name = name.replace(".vm", ""); |
||||
|
String[] nameParts = name.split("\\."); |
||||
|
if (nameParts.length >= 1) { |
||||
|
nameBuilder.append(nameParts[0]); |
||||
|
for (int i = 1; i < nameParts.length; i++) { |
||||
|
String word = WordUtils.capitalize(nameParts[i]); |
||||
|
nameBuilder.append(word); |
||||
|
} |
||||
|
return nameBuilder.toString(); |
||||
|
} else { |
||||
|
throw new Exception("Error during filename validation"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private AdminSettings convertToTenantAdminSettings(String key, ObjectNode jsonValue) { |
||||
|
AdminSettings tenantMailSettings = new AdminSettings(); |
||||
|
jsonValue.put("useSystemMailSettings", true); |
||||
|
tenantMailSettings.setJsonValue(jsonValue); |
||||
|
tenantMailSettings.setKey(key); |
||||
|
return tenantMailSettings; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public ListenableFuture<Void> processRuleChainMetadataRequestMsg(TenantId tenantId, Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg) { |
||||
|
log.trace("[{}] processRuleChainMetadataRequestMsg [{}][{}]", tenantId, edge.getName(), ruleChainMetadataRequestMsg); |
||||
|
SettableFuture<Void> futureToSet = SettableFuture.create(); |
||||
|
if (ruleChainMetadataRequestMsg.getRuleChainIdMSB() != 0 && ruleChainMetadataRequestMsg.getRuleChainIdLSB() != 0) { |
||||
|
RuleChainId ruleChainId = |
||||
|
new RuleChainId(new UUID(ruleChainMetadataRequestMsg.getRuleChainIdMSB(), ruleChainMetadataRequestMsg.getRuleChainIdLSB())); |
||||
|
ListenableFuture<EdgeEvent> future = saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.RULE_CHAIN_METADATA, EdgeEventActionType.ADDED, ruleChainId, null); |
||||
|
Futures.addCallback(future, new FutureCallback<EdgeEvent>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable EdgeEvent result) { |
||||
|
futureToSet.set(null); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.error("Can't save edge event [{}]", ruleChainMetadataRequestMsg, t); |
||||
|
futureToSet.setException(t); |
||||
|
} |
||||
|
}, dbCallbackExecutorService); |
||||
|
} |
||||
|
return futureToSet; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public ListenableFuture<Void> processAttributesRequestMsg(TenantId tenantId, Edge edge, AttributesRequestMsg attributesRequestMsg) { |
||||
|
log.trace("[{}] processAttributesRequestMsg [{}][{}]", tenantId, edge.getName(), attributesRequestMsg); |
||||
|
EntityId entityId = EntityIdFactory.getByTypeAndUuid( |
||||
|
EntityType.valueOf(attributesRequestMsg.getEntityType()), |
||||
|
new UUID(attributesRequestMsg.getEntityIdMSB(), attributesRequestMsg.getEntityIdLSB())); |
||||
|
final EdgeEventType type = EdgeUtils.getEdgeEventTypeByEntityType(entityId.getEntityType()); |
||||
|
if (type != null) { |
||||
|
SettableFuture<Void> futureToSet = SettableFuture.create(); |
||||
|
String scope = attributesRequestMsg.getScope(); |
||||
|
ListenableFuture<List<AttributeKvEntry>> ssAttrFuture = attributesService.findAll(tenantId, entityId, scope); |
||||
|
Futures.addCallback(ssAttrFuture, new FutureCallback<List<AttributeKvEntry>>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable List<AttributeKvEntry> ssAttributes) { |
||||
|
if (ssAttributes != null && !ssAttributes.isEmpty()) { |
||||
|
try { |
||||
|
Map<String, Object> entityData = new HashMap<>(); |
||||
|
ObjectNode attributes = mapper.createObjectNode(); |
||||
|
for (AttributeKvEntry attr : ssAttributes) { |
||||
|
if (attr.getDataType() == DataType.BOOLEAN && attr.getBooleanValue().isPresent()) { |
||||
|
attributes.put(attr.getKey(), attr.getBooleanValue().get()); |
||||
|
} else if (attr.getDataType() == DataType.DOUBLE && attr.getDoubleValue().isPresent()) { |
||||
|
attributes.put(attr.getKey(), attr.getDoubleValue().get()); |
||||
|
} else if (attr.getDataType() == DataType.LONG && attr.getLongValue().isPresent()) { |
||||
|
attributes.put(attr.getKey(), attr.getLongValue().get()); |
||||
|
} else { |
||||
|
attributes.put(attr.getKey(), attr.getValueAsString()); |
||||
|
} |
||||
|
} |
||||
|
entityData.put("kv", attributes); |
||||
|
entityData.put("scope", scope); |
||||
|
JsonNode body = mapper.valueToTree(entityData); |
||||
|
log.debug("Sending attributes data msg, entityId [{}], attributes [{}]", entityId, body); |
||||
|
saveEdgeEvent(tenantId, |
||||
|
edge.getId(), |
||||
|
type, |
||||
|
EdgeEventActionType.ATTRIBUTES_UPDATED, |
||||
|
entityId, |
||||
|
body); |
||||
|
} catch (Exception e) { |
||||
|
log.error("[{}] Failed to send attribute updates to the edge", edge.getName(), e); |
||||
|
throw new RuntimeException("[" + edge.getName() + "] Failed to send attribute updates to the edge", e); |
||||
|
} |
||||
|
} else { |
||||
|
log.trace("[{}][{}] No attributes found for entity {} [{}]", tenantId, |
||||
|
edge.getName(), |
||||
|
entityId.getEntityType(), |
||||
|
entityId.getId()); |
||||
|
} |
||||
|
futureToSet.set(null); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.error("Can't save attributes [{}]", attributesRequestMsg, t); |
||||
|
futureToSet.setException(t); |
||||
|
} |
||||
|
}, dbCallbackExecutorService); |
||||
|
return futureToSet; |
||||
|
} else { |
||||
|
log.warn("[{}] Type doesn't supported {}", tenantId, entityId.getEntityType()); |
||||
|
return Futures.immediateFuture(null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public ListenableFuture<Void> processRelationRequestMsg(TenantId tenantId, Edge edge, RelationRequestMsg relationRequestMsg) { |
||||
|
log.trace("[{}] processRelationRequestMsg [{}][{}]", tenantId, edge.getName(), relationRequestMsg); |
||||
|
EntityId entityId = EntityIdFactory.getByTypeAndUuid( |
||||
|
EntityType.valueOf(relationRequestMsg.getEntityType()), |
||||
|
new UUID(relationRequestMsg.getEntityIdMSB(), relationRequestMsg.getEntityIdLSB())); |
||||
|
|
||||
|
List<ListenableFuture<List<EntityRelation>>> futures = new ArrayList<>(); |
||||
|
futures.add(findRelationByQuery(tenantId, edge, entityId, EntitySearchDirection.FROM)); |
||||
|
futures.add(findRelationByQuery(tenantId, edge, entityId, EntitySearchDirection.TO)); |
||||
|
ListenableFuture<List<List<EntityRelation>>> relationsListFuture = Futures.allAsList(futures); |
||||
|
SettableFuture<Void> futureToSet = SettableFuture.create(); |
||||
|
Futures.addCallback(relationsListFuture, new FutureCallback<List<List<EntityRelation>>>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable List<List<EntityRelation>> relationsList) { |
||||
|
try { |
||||
|
if (relationsList != null && !relationsList.isEmpty()) { |
||||
|
for (List<EntityRelation> entityRelations : relationsList) { |
||||
|
log.trace("[{}] [{}] [{}] relation(s) are going to be pushed to edge.", edge.getId(), entityId, entityRelations.size()); |
||||
|
for (EntityRelation relation : entityRelations) { |
||||
|
try { |
||||
|
if (!relation.getFrom().getEntityType().equals(EntityType.EDGE) && |
||||
|
!relation.getTo().getEntityType().equals(EntityType.EDGE)) { |
||||
|
saveEdgeEvent(tenantId, |
||||
|
edge.getId(), |
||||
|
EdgeEventType.RELATION, |
||||
|
EdgeEventActionType.ADDED, |
||||
|
null, |
||||
|
mapper.valueToTree(relation)); |
||||
|
} |
||||
|
} catch (Exception e) { |
||||
|
log.error("Exception during loading relation [{}] to edge on sync!", relation, e); |
||||
|
futureToSet.setException(e); |
||||
|
return; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
futureToSet.set(null); |
||||
|
} catch (Exception e) { |
||||
|
log.error("Exception during loading relation(s) to edge on sync!", e); |
||||
|
futureToSet.setException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.error("[{}] Can't find relation by query. Entity id [{}]", tenantId, entityId, t); |
||||
|
futureToSet.setException(t); |
||||
|
} |
||||
|
}, dbCallbackExecutorService); |
||||
|
return futureToSet; |
||||
|
} |
||||
|
|
||||
|
private ListenableFuture<List<EntityRelation>> findRelationByQuery(TenantId tenantId, Edge edge, EntityId entityId, EntitySearchDirection direction) { |
||||
|
EntityRelationsQuery query = new EntityRelationsQuery(); |
||||
|
query.setParameters(new RelationsSearchParameters(entityId, direction, -1, false)); |
||||
|
return relationService.findByQuery(tenantId, query); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public ListenableFuture<Void> processDeviceCredentialsRequestMsg(TenantId tenantId, Edge edge, DeviceCredentialsRequestMsg deviceCredentialsRequestMsg) { |
||||
|
log.trace("[{}] processDeviceCredentialsRequestMsg [{}][{}]", tenantId, edge.getName(), deviceCredentialsRequestMsg); |
||||
|
SettableFuture<Void> futureToSet = SettableFuture.create(); |
||||
|
if (deviceCredentialsRequestMsg.getDeviceIdMSB() != 0 && deviceCredentialsRequestMsg.getDeviceIdLSB() != 0) { |
||||
|
DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsRequestMsg.getDeviceIdMSB(), deviceCredentialsRequestMsg.getDeviceIdLSB())); |
||||
|
ListenableFuture<EdgeEvent> future = saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_UPDATED, deviceId, null); |
||||
|
Futures.addCallback(future, new FutureCallback<EdgeEvent>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable EdgeEvent result) { |
||||
|
futureToSet.set(null); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.error("Can't save edge event [{}]", deviceCredentialsRequestMsg, t); |
||||
|
futureToSet.setException(t); |
||||
|
} |
||||
|
}, dbCallbackExecutorService); |
||||
|
} |
||||
|
return futureToSet; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public ListenableFuture<Void> processUserCredentialsRequestMsg(TenantId tenantId, Edge edge, UserCredentialsRequestMsg userCredentialsRequestMsg) { |
||||
|
log.trace("[{}] processUserCredentialsRequestMsg [{}][{}]", tenantId, edge.getName(), userCredentialsRequestMsg); |
||||
|
SettableFuture<Void> futureToSet = SettableFuture.create(); |
||||
|
if (userCredentialsRequestMsg.getUserIdMSB() != 0 && userCredentialsRequestMsg.getUserIdLSB() != 0) { |
||||
|
UserId userId = new UserId(new UUID(userCredentialsRequestMsg.getUserIdMSB(), userCredentialsRequestMsg.getUserIdLSB())); |
||||
|
ListenableFuture<EdgeEvent> future = saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.USER, EdgeEventActionType.CREDENTIALS_UPDATED, userId, null); |
||||
|
Futures.addCallback(future, new FutureCallback<EdgeEvent>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable EdgeEvent result) { |
||||
|
futureToSet.set(null); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.error("Can't save edge event [{}]", userCredentialsRequestMsg, t); |
||||
|
futureToSet.setException(t); |
||||
|
} |
||||
|
}, dbCallbackExecutorService); |
||||
|
} |
||||
|
return futureToSet; |
||||
|
} |
||||
|
|
||||
|
private ListenableFuture<EdgeEvent> saveEdgeEvent(TenantId tenantId, |
||||
|
EdgeId edgeId, |
||||
|
EdgeEventType type, |
||||
|
EdgeEventActionType action, |
||||
|
EntityId entityId, |
||||
|
JsonNode body) { |
||||
|
log.trace("Pushing edge event to edge queue. tenantId [{}], edgeId [{}], type [{}], action[{}], entityId [{}], body [{}]", |
||||
|
tenantId, edgeId, type, action, entityId, body); |
||||
|
|
||||
|
EdgeEvent edgeEvent = new EdgeEvent(); |
||||
|
edgeEvent.setTenantId(tenantId); |
||||
|
edgeEvent.setEdgeId(edgeId); |
||||
|
edgeEvent.setType(type); |
||||
|
edgeEvent.setAction(action); |
||||
|
if (entityId != null) { |
||||
|
edgeEvent.setEntityId(entityId.getId()); |
||||
|
} |
||||
|
edgeEvent.setBody(body); |
||||
|
ListenableFuture<EdgeEvent> future = edgeEventService.saveAsync(edgeEvent); |
||||
|
Futures.addCallback(future, new FutureCallback<EdgeEvent>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable EdgeEvent result) { |
||||
|
tbClusterService.onEdgeEventUpdate(tenantId, edgeId); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.warn("[{}] Can't save edge event [{}] for edge [{}]", tenantId.getId(), edgeEvent, edgeId.getId(), t); |
||||
|
} |
||||
|
}, dbCallbackExecutorService); |
||||
|
return future; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,40 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.init; |
||||
|
|
||||
|
import com.google.common.util.concurrent.ListenableFuture; |
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.gen.edge.AttributesRequestMsg; |
||||
|
import org.thingsboard.server.gen.edge.DeviceCredentialsRequestMsg; |
||||
|
import org.thingsboard.server.gen.edge.RelationRequestMsg; |
||||
|
import org.thingsboard.server.gen.edge.RuleChainMetadataRequestMsg; |
||||
|
import org.thingsboard.server.gen.edge.UserCredentialsRequestMsg; |
||||
|
|
||||
|
public interface SyncEdgeService { |
||||
|
|
||||
|
void sync(TenantId tenantId, Edge edge); |
||||
|
|
||||
|
ListenableFuture<Void> processRuleChainMetadataRequestMsg(TenantId tenantId, Edge edge, RuleChainMetadataRequestMsg ruleChainMetadataRequestMsg); |
||||
|
|
||||
|
ListenableFuture<Void> processAttributesRequestMsg(TenantId tenantId, Edge edge, AttributesRequestMsg attributesRequestMsg); |
||||
|
|
||||
|
ListenableFuture<Void> processRelationRequestMsg(TenantId tenantId, Edge edge, RelationRequestMsg relationRequestMsg); |
||||
|
|
||||
|
ListenableFuture<Void> processDeviceCredentialsRequestMsg(TenantId tenantId, Edge edge, DeviceCredentialsRequestMsg deviceCredentialsRequestMsg); |
||||
|
|
||||
|
ListenableFuture<Void> processUserCredentialsRequestMsg(TenantId tenantId, Edge edge, UserCredentialsRequestMsg userCredentialsRequestMsg); |
||||
|
} |
||||
@ -0,0 +1,99 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.processor; |
||||
|
|
||||
|
import com.google.common.util.concurrent.Futures; |
||||
|
import com.google.common.util.concurrent.ListenableFuture; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.EntityType; |
||||
|
import org.thingsboard.server.common.data.alarm.Alarm; |
||||
|
import org.thingsboard.server.common.data.alarm.AlarmSeverity; |
||||
|
import org.thingsboard.server.common.data.alarm.AlarmStatus; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.gen.edge.AlarmUpdateMsg; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
@Component |
||||
|
@Slf4j |
||||
|
@TbCoreComponent |
||||
|
public class AlarmProcessor extends BaseProcessor { |
||||
|
|
||||
|
public ListenableFuture<Void> onAlarmUpdate(TenantId tenantId, AlarmUpdateMsg alarmUpdateMsg) { |
||||
|
log.trace("[{}] onAlarmUpdate [{}]", tenantId, alarmUpdateMsg); |
||||
|
EntityId originatorId = getAlarmOriginator(tenantId, alarmUpdateMsg.getOriginatorName(), |
||||
|
EntityType.valueOf(alarmUpdateMsg.getOriginatorType())); |
||||
|
if (originatorId == null) { |
||||
|
return Futures.immediateFuture(null); |
||||
|
} |
||||
|
try { |
||||
|
Alarm existentAlarm = alarmService.findLatestByOriginatorAndType(tenantId, originatorId, alarmUpdateMsg.getType()).get(); |
||||
|
switch (alarmUpdateMsg.getMsgType()) { |
||||
|
case ENTITY_CREATED_RPC_MESSAGE: |
||||
|
case ENTITY_UPDATED_RPC_MESSAGE: |
||||
|
if (existentAlarm == null || existentAlarm.getStatus().isCleared()) { |
||||
|
existentAlarm = new Alarm(); |
||||
|
existentAlarm.setTenantId(tenantId); |
||||
|
existentAlarm.setType(alarmUpdateMsg.getName()); |
||||
|
existentAlarm.setOriginator(originatorId); |
||||
|
existentAlarm.setSeverity(AlarmSeverity.valueOf(alarmUpdateMsg.getSeverity())); |
||||
|
existentAlarm.setStartTs(alarmUpdateMsg.getStartTs()); |
||||
|
existentAlarm.setClearTs(alarmUpdateMsg.getClearTs()); |
||||
|
existentAlarm.setPropagate(alarmUpdateMsg.getPropagate()); |
||||
|
} |
||||
|
existentAlarm.setStatus(AlarmStatus.valueOf(alarmUpdateMsg.getStatus())); |
||||
|
existentAlarm.setAckTs(alarmUpdateMsg.getAckTs()); |
||||
|
existentAlarm.setEndTs(alarmUpdateMsg.getEndTs()); |
||||
|
existentAlarm.setDetails(mapper.readTree(alarmUpdateMsg.getDetails())); |
||||
|
alarmService.createOrUpdateAlarm(existentAlarm); |
||||
|
break; |
||||
|
case ALARM_ACK_RPC_MESSAGE: |
||||
|
if (existentAlarm != null) { |
||||
|
alarmService.ackAlarm(tenantId, existentAlarm.getId(), alarmUpdateMsg.getAckTs()); |
||||
|
} |
||||
|
break; |
||||
|
case ALARM_CLEAR_RPC_MESSAGE: |
||||
|
if (existentAlarm != null) { |
||||
|
alarmService.clearAlarm(tenantId, existentAlarm.getId(), mapper.readTree(alarmUpdateMsg.getDetails()), alarmUpdateMsg.getAckTs()); |
||||
|
} |
||||
|
break; |
||||
|
case ENTITY_DELETED_RPC_MESSAGE: |
||||
|
if (existentAlarm != null) { |
||||
|
alarmService.deleteAlarm(tenantId, existentAlarm.getId()); |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
return Futures.immediateFuture(null); |
||||
|
} catch (Exception e) { |
||||
|
log.error("Failed to process alarm update msg [{}]", alarmUpdateMsg, e); |
||||
|
return Futures.immediateFailedFuture(new RuntimeException("Failed to process alarm update msg", e)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private EntityId getAlarmOriginator(TenantId tenantId, String entityName, EntityType entityType) { |
||||
|
switch (entityType) { |
||||
|
case DEVICE: |
||||
|
return deviceService.findDeviceByTenantIdAndName(tenantId, entityName).getId(); |
||||
|
case ASSET: |
||||
|
return assetService.findAssetByTenantIdAndName(tenantId, entityName).getId(); |
||||
|
case ENTITY_VIEW: |
||||
|
return entityViewService.findEntityViewByTenantIdAndName(tenantId, entityName).getId(); |
||||
|
default: |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,136 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.processor; |
||||
|
|
||||
|
import com.fasterxml.jackson.databind.JsonNode; |
||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
|
import com.google.common.util.concurrent.FutureCallback; |
||||
|
import com.google.common.util.concurrent.Futures; |
||||
|
import com.google.common.util.concurrent.ListenableFuture; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.checkerframework.checker.nullness.qual.Nullable; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEvent; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventType; |
||||
|
import org.thingsboard.server.common.data.id.EdgeId; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.dao.alarm.AlarmService; |
||||
|
import org.thingsboard.server.dao.asset.AssetService; |
||||
|
import org.thingsboard.server.dao.attributes.AttributesService; |
||||
|
import org.thingsboard.server.dao.customer.CustomerService; |
||||
|
import org.thingsboard.server.dao.dashboard.DashboardService; |
||||
|
import org.thingsboard.server.dao.device.DeviceCredentialsService; |
||||
|
import org.thingsboard.server.dao.device.DeviceService; |
||||
|
import org.thingsboard.server.dao.edge.EdgeEventService; |
||||
|
import org.thingsboard.server.dao.edge.EdgeService; |
||||
|
import org.thingsboard.server.dao.entityview.EntityViewService; |
||||
|
import org.thingsboard.server.dao.relation.RelationService; |
||||
|
import org.thingsboard.server.dao.user.UserService; |
||||
|
import org.thingsboard.server.service.executors.DbCallbackExecutorService; |
||||
|
import org.thingsboard.server.service.profile.DefaultTbDeviceProfileCache; |
||||
|
import org.thingsboard.server.service.profile.TbDeviceProfileCache; |
||||
|
import org.thingsboard.server.service.queue.TbClusterService; |
||||
|
import org.thingsboard.server.service.state.DeviceStateService; |
||||
|
|
||||
|
@Slf4j |
||||
|
public abstract class BaseProcessor { |
||||
|
|
||||
|
protected static final ObjectMapper mapper = new ObjectMapper(); |
||||
|
|
||||
|
@Autowired |
||||
|
protected AlarmService alarmService; |
||||
|
|
||||
|
@Autowired |
||||
|
protected DeviceService deviceService; |
||||
|
|
||||
|
@Autowired |
||||
|
protected TbDeviceProfileCache deviceProfileCache; |
||||
|
|
||||
|
@Autowired |
||||
|
protected DashboardService dashboardService; |
||||
|
|
||||
|
@Autowired |
||||
|
protected AssetService assetService; |
||||
|
|
||||
|
@Autowired |
||||
|
protected EntityViewService entityViewService; |
||||
|
|
||||
|
@Autowired |
||||
|
protected EdgeService edgeService; |
||||
|
|
||||
|
@Autowired |
||||
|
protected CustomerService customerService; |
||||
|
|
||||
|
@Autowired |
||||
|
protected UserService userService; |
||||
|
|
||||
|
@Autowired |
||||
|
protected RelationService relationService; |
||||
|
|
||||
|
@Autowired |
||||
|
protected DeviceCredentialsService deviceCredentialsService; |
||||
|
|
||||
|
@Autowired |
||||
|
protected AttributesService attributesService; |
||||
|
|
||||
|
@Autowired |
||||
|
protected TbClusterService tbClusterService; |
||||
|
|
||||
|
@Autowired |
||||
|
protected DeviceStateService deviceStateService; |
||||
|
|
||||
|
@Autowired |
||||
|
protected EdgeEventService edgeEventService; |
||||
|
|
||||
|
@Autowired |
||||
|
protected DbCallbackExecutorService dbCallbackExecutorService; |
||||
|
|
||||
|
protected ListenableFuture<EdgeEvent> saveEdgeEvent(TenantId tenantId, |
||||
|
EdgeId edgeId, |
||||
|
EdgeEventType type, |
||||
|
EdgeEventActionType action, |
||||
|
EntityId entityId, |
||||
|
JsonNode body) { |
||||
|
log.debug("Pushing event to edge queue. tenantId [{}], edgeId [{}], type[{}], " + |
||||
|
"action [{}], entityId [{}], body [{}]", |
||||
|
tenantId, edgeId, type, action, entityId, body); |
||||
|
|
||||
|
EdgeEvent edgeEvent = new EdgeEvent(); |
||||
|
edgeEvent.setTenantId(tenantId); |
||||
|
edgeEvent.setEdgeId(edgeId); |
||||
|
edgeEvent.setType(type); |
||||
|
edgeEvent.setAction(action); |
||||
|
if (entityId != null) { |
||||
|
edgeEvent.setEntityId(entityId.getId()); |
||||
|
} |
||||
|
edgeEvent.setBody(body); |
||||
|
ListenableFuture<EdgeEvent> future = edgeEventService.saveAsync(edgeEvent); |
||||
|
Futures.addCallback(future, new FutureCallback<EdgeEvent>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable EdgeEvent result) { |
||||
|
tbClusterService.onEdgeEventUpdate(tenantId, edgeId); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.warn("[{}] Can't save edge event [{}] for edge [{}]", tenantId.getId(), edgeEvent, edgeId.getId(), t); |
||||
|
} |
||||
|
}, dbCallbackExecutorService); |
||||
|
return future; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,299 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.processor; |
||||
|
|
||||
|
import com.fasterxml.jackson.core.JsonProcessingException; |
||||
|
import com.fasterxml.jackson.databind.node.ObjectNode; |
||||
|
import com.google.common.util.concurrent.FutureCallback; |
||||
|
import com.google.common.util.concurrent.Futures; |
||||
|
import com.google.common.util.concurrent.ListenableFuture; |
||||
|
import com.google.common.util.concurrent.SettableFuture; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.lang3.RandomStringUtils; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
import org.checkerframework.checker.nullness.qual.Nullable; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.rule.engine.api.RpcError; |
||||
|
import org.thingsboard.server.common.data.Customer; |
||||
|
import org.thingsboard.server.common.data.DataConstants; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventType; |
||||
|
import org.thingsboard.server.common.data.id.CustomerId; |
||||
|
import org.thingsboard.server.common.data.id.DeviceId; |
||||
|
import org.thingsboard.server.common.data.id.DeviceProfileId; |
||||
|
import org.thingsboard.server.common.data.id.EdgeId; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.relation.EntityRelation; |
||||
|
import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
||||
|
import org.thingsboard.server.common.data.security.DeviceCredentials; |
||||
|
import org.thingsboard.server.common.data.security.DeviceCredentialsType; |
||||
|
import org.thingsboard.server.common.msg.TbMsg; |
||||
|
import org.thingsboard.server.common.msg.TbMsgDataType; |
||||
|
import org.thingsboard.server.common.msg.TbMsgMetaData; |
||||
|
import org.thingsboard.server.dao.model.ModelConstants; |
||||
|
import org.thingsboard.common.util.JacksonUtil; |
||||
|
import org.thingsboard.server.gen.edge.DeviceCredentialsUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.DeviceRpcCallMsg; |
||||
|
import org.thingsboard.server.gen.edge.DeviceUpdateMsg; |
||||
|
import org.thingsboard.server.queue.TbQueueCallback; |
||||
|
import org.thingsboard.server.queue.TbQueueMsgMetadata; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.rpc.FromDeviceRpcResponse; |
||||
|
import org.thingsboard.server.service.rpc.FromDeviceRpcResponseActorMsg; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.UUID; |
||||
|
import java.util.concurrent.locks.ReentrantLock; |
||||
|
|
||||
|
@Component |
||||
|
@Slf4j |
||||
|
@TbCoreComponent |
||||
|
public class DeviceProcessor extends BaseProcessor { |
||||
|
|
||||
|
private static final ReentrantLock deviceCreationLock = new ReentrantLock(); |
||||
|
|
||||
|
public ListenableFuture<Void> onDeviceUpdate(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg) { |
||||
|
log.trace("[{}] onDeviceUpdate [{}] from edge [{}]", tenantId, deviceUpdateMsg, edge.getName()); |
||||
|
switch (deviceUpdateMsg.getMsgType()) { |
||||
|
case ENTITY_CREATED_RPC_MESSAGE: |
||||
|
String deviceName = deviceUpdateMsg.getName(); |
||||
|
Device device = deviceService.findDeviceByTenantIdAndName(tenantId, deviceName); |
||||
|
if (device != null) { |
||||
|
ListenableFuture<List<EdgeId>> future = edgeService.findRelatedEdgeIdsByEntityId(tenantId, device.getId()); |
||||
|
SettableFuture<Void> futureToSet = SettableFuture.create(); |
||||
|
Futures.addCallback(future, new FutureCallback<List<EdgeId>>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable List<EdgeId> edgeIds) { |
||||
|
boolean update = false; |
||||
|
if (edgeIds != null && !edgeIds.isEmpty()) { |
||||
|
if (edgeIds.contains(edge.getId())) { |
||||
|
update = true; |
||||
|
} |
||||
|
} |
||||
|
Device device; |
||||
|
if (update) { |
||||
|
log.info("[{}] Device with name '{}' already exists on the cloud, and related to this edge [{}]. " + |
||||
|
"deviceUpdateMsg [{}], Updating device", tenantId, deviceName, edge.getId(), deviceUpdateMsg); |
||||
|
updateDevice(tenantId, edge, deviceUpdateMsg); |
||||
|
} else { |
||||
|
log.info("[{}] Device with name '{}' already exists on the cloud, but not related to this edge [{}]. deviceUpdateMsg [{}]." + |
||||
|
"Creating a new device with random prefix and relate to this edge", tenantId, deviceName, edge.getId(), deviceUpdateMsg); |
||||
|
String newDeviceName = deviceUpdateMsg.getName() + "_" + RandomStringUtils.randomAlphabetic(15); |
||||
|
device = createDevice(tenantId, edge, deviceUpdateMsg, newDeviceName); |
||||
|
ObjectNode body = mapper.createObjectNode(); |
||||
|
body.put("conflictName", deviceName); |
||||
|
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, device.getId(), body); |
||||
|
} |
||||
|
futureToSet.set(null); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.error("[{}] Failed to get related edge ids by device id [{}], edge [{}]", tenantId, deviceUpdateMsg, edge.getId(), t); |
||||
|
futureToSet.setException(t); |
||||
|
} |
||||
|
}, dbCallbackExecutorService); |
||||
|
return futureToSet; |
||||
|
} else { |
||||
|
log.info("[{}] Creating new device and replacing device entity on the edge [{}]", tenantId, deviceUpdateMsg); |
||||
|
device = createDevice(tenantId, edge, deviceUpdateMsg, deviceUpdateMsg.getName()); |
||||
|
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.ENTITY_MERGE_REQUEST, device.getId(), null); |
||||
|
} |
||||
|
break; |
||||
|
case ENTITY_UPDATED_RPC_MESSAGE: |
||||
|
updateDevice(tenantId, edge, deviceUpdateMsg); |
||||
|
break; |
||||
|
case ENTITY_DELETED_RPC_MESSAGE: |
||||
|
DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB())); |
||||
|
Device deviceToDelete = deviceService.findDeviceById(tenantId, deviceId); |
||||
|
if (deviceToDelete != null) { |
||||
|
deviceService.unassignDeviceFromEdge(tenantId, deviceId, edge.getId()); |
||||
|
} |
||||
|
break; |
||||
|
case UNRECOGNIZED: |
||||
|
log.error("Unsupported msg type {}", deviceUpdateMsg.getMsgType()); |
||||
|
return Futures.immediateFailedFuture(new RuntimeException("Unsupported msg type " + deviceUpdateMsg.getMsgType())); |
||||
|
} |
||||
|
return Futures.immediateFuture(null); |
||||
|
} |
||||
|
|
||||
|
public ListenableFuture<Void> onDeviceCredentialsUpdate(TenantId tenantId, DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) { |
||||
|
log.debug("Executing onDeviceCredentialsUpdate, deviceCredentialsUpdateMsg [{}]", deviceCredentialsUpdateMsg); |
||||
|
DeviceId deviceId = new DeviceId(new UUID(deviceCredentialsUpdateMsg.getDeviceIdMSB(), deviceCredentialsUpdateMsg.getDeviceIdLSB())); |
||||
|
ListenableFuture<Device> deviceFuture = deviceService.findDeviceByIdAsync(tenantId, deviceId); |
||||
|
return Futures.transform(deviceFuture, device -> { |
||||
|
if (device != null) { |
||||
|
log.debug("Updating device credentials for device [{}]. New device credentials Id [{}], value [{}]", |
||||
|
device.getName(), deviceCredentialsUpdateMsg.getCredentialsId(), deviceCredentialsUpdateMsg.getCredentialsValue()); |
||||
|
try { |
||||
|
DeviceCredentials deviceCredentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(tenantId, device.getId()); |
||||
|
deviceCredentials.setCredentialsType(DeviceCredentialsType.valueOf(deviceCredentialsUpdateMsg.getCredentialsType())); |
||||
|
deviceCredentials.setCredentialsId(deviceCredentialsUpdateMsg.getCredentialsId()); |
||||
|
deviceCredentials.setCredentialsValue(deviceCredentialsUpdateMsg.getCredentialsValue()); |
||||
|
deviceCredentialsService.updateDeviceCredentials(tenantId, deviceCredentials); |
||||
|
} catch (Exception e) { |
||||
|
log.error("Can't update device credentials for device [{}], deviceCredentialsUpdateMsg [{}]", device.getName(), deviceCredentialsUpdateMsg, e); |
||||
|
throw new RuntimeException(e); |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
}, dbCallbackExecutorService); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
private void updateDevice(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg) { |
||||
|
DeviceId deviceId = new DeviceId(new UUID(deviceUpdateMsg.getIdMSB(), deviceUpdateMsg.getIdLSB())); |
||||
|
Device device = deviceService.findDeviceById(tenantId, deviceId); |
||||
|
if (device != null) { |
||||
|
device.setName(deviceUpdateMsg.getName()); |
||||
|
device.setType(deviceUpdateMsg.getType()); |
||||
|
device.setLabel(deviceUpdateMsg.getLabel()); |
||||
|
device.setAdditionalInfo(JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo())); |
||||
|
if (deviceUpdateMsg.getDeviceProfileIdMSB() != 0 && deviceUpdateMsg.getDeviceProfileIdLSB() != 0) { |
||||
|
DeviceProfileId deviceProfileId = new DeviceProfileId( |
||||
|
new UUID(deviceUpdateMsg.getDeviceProfileIdMSB(), deviceUpdateMsg.getDeviceProfileIdLSB())); |
||||
|
device.setDeviceProfileId(deviceProfileId); |
||||
|
} |
||||
|
deviceService.saveDevice(device); |
||||
|
saveEdgeEvent(tenantId, edge.getId(), EdgeEventType.DEVICE, EdgeEventActionType.CREDENTIALS_REQUEST, deviceId, null); |
||||
|
} else { |
||||
|
log.warn("[{}] can't find device [{}], edge [{}]", tenantId, deviceUpdateMsg, edge.getId()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private Device createDevice(TenantId tenantId, Edge edge, DeviceUpdateMsg deviceUpdateMsg, String deviceName) { |
||||
|
Device device; |
||||
|
try { |
||||
|
deviceCreationLock.lock(); |
||||
|
log.debug("[{}] Creating device entity [{}] from edge [{}]", tenantId, deviceUpdateMsg, edge.getName()); |
||||
|
device = new Device(); |
||||
|
device.setTenantId(edge.getTenantId()); |
||||
|
// make device private, if edge is public
|
||||
|
device.setCustomerId(getCustomerId(edge)); |
||||
|
device.setName(deviceName); |
||||
|
device.setType(deviceUpdateMsg.getType()); |
||||
|
device.setLabel(deviceUpdateMsg.getLabel()); |
||||
|
device.setAdditionalInfo(JacksonUtil.toJsonNode(deviceUpdateMsg.getAdditionalInfo())); |
||||
|
if (deviceUpdateMsg.getDeviceProfileIdMSB() != 0 && deviceUpdateMsg.getDeviceProfileIdLSB() != 0) { |
||||
|
DeviceProfileId deviceProfileId = new DeviceProfileId( |
||||
|
new UUID(deviceUpdateMsg.getDeviceProfileIdMSB(), deviceUpdateMsg.getDeviceProfileIdLSB())); |
||||
|
device.setDeviceProfileId(deviceProfileId); |
||||
|
} |
||||
|
device = deviceService.saveDevice(device); |
||||
|
createRelationFromEdge(tenantId, edge.getId(), device.getId()); |
||||
|
deviceStateService.onDeviceAdded(device); |
||||
|
pushDeviceCreatedEventToRuleEngine(tenantId, edge, device); |
||||
|
deviceService.assignDeviceToEdge(edge.getTenantId(), device.getId(), edge.getId()); |
||||
|
} finally { |
||||
|
deviceCreationLock.unlock(); |
||||
|
} |
||||
|
return device; |
||||
|
} |
||||
|
|
||||
|
private CustomerId getCustomerId(Edge edge) { |
||||
|
if (edge.getCustomerId() == null || edge.getCustomerId().getId().equals(ModelConstants.NULL_UUID)) { |
||||
|
return edge.getCustomerId(); |
||||
|
} |
||||
|
Customer publicCustomer = customerService.findOrCreatePublicCustomer(edge.getTenantId()); |
||||
|
if (publicCustomer.getId().equals(edge.getCustomerId())) { |
||||
|
return null; |
||||
|
} else { |
||||
|
return edge.getCustomerId(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void createRelationFromEdge(TenantId tenantId, EdgeId edgeId, EntityId entityId) { |
||||
|
EntityRelation relation = new EntityRelation(); |
||||
|
relation.setFrom(edgeId); |
||||
|
relation.setTo(entityId); |
||||
|
relation.setTypeGroup(RelationTypeGroup.COMMON); |
||||
|
relation.setType(EntityRelation.EDGE_TYPE); |
||||
|
relationService.saveRelation(tenantId, relation); |
||||
|
} |
||||
|
|
||||
|
private void pushDeviceCreatedEventToRuleEngine(TenantId tenantId, Edge edge, Device device) { |
||||
|
try { |
||||
|
DeviceId deviceId = device.getId(); |
||||
|
ObjectNode entityNode = mapper.valueToTree(device); |
||||
|
TbMsg tbMsg = TbMsg.newMsg(DataConstants.ENTITY_CREATED, deviceId, |
||||
|
getActionTbMsgMetaData(edge, device.getCustomerId()), TbMsgDataType.JSON, mapper.writeValueAsString(entityNode)); |
||||
|
tbClusterService.pushMsgToRuleEngine(tenantId, deviceId, tbMsg, new TbQueueCallback() { |
||||
|
@Override |
||||
|
public void onSuccess(TbQueueMsgMetadata metadata) { |
||||
|
log.debug("Successfully send ENTITY_CREATED EVENT to rule engine [{}]", device); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.debug("Failed to send ENTITY_CREATED EVENT to rule engine [{}]", device, t); |
||||
|
} |
||||
|
}); |
||||
|
} catch (JsonProcessingException | IllegalArgumentException e) { |
||||
|
log.warn("[{}] Failed to push device action to rule engine: {}", device.getId(), DataConstants.ENTITY_CREATED, e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private TbMsgMetaData getActionTbMsgMetaData(Edge edge, CustomerId customerId) { |
||||
|
TbMsgMetaData metaData = getTbMsgMetaData(edge); |
||||
|
if (customerId != null && !customerId.isNullUid()) { |
||||
|
metaData.putValue("customerId", customerId.toString()); |
||||
|
} |
||||
|
return metaData; |
||||
|
} |
||||
|
|
||||
|
private TbMsgMetaData getTbMsgMetaData(Edge edge) { |
||||
|
TbMsgMetaData metaData = new TbMsgMetaData(); |
||||
|
metaData.putValue("edgeId", edge.getId().toString()); |
||||
|
metaData.putValue("edgeName", edge.getName()); |
||||
|
return metaData; |
||||
|
} |
||||
|
|
||||
|
public ListenableFuture<Void> processDeviceRpcCallResponseMsg(TenantId tenantId, DeviceRpcCallMsg deviceRpcCallMsg) { |
||||
|
log.trace("[{}] processDeviceRpcCallResponseMsg [{}]", tenantId, deviceRpcCallMsg); |
||||
|
SettableFuture<Void> futureToSet = SettableFuture.create(); |
||||
|
UUID requestUuid = new UUID(deviceRpcCallMsg.getRequestUuidMSB(), deviceRpcCallMsg.getRequestUuidLSB()); |
||||
|
DeviceId deviceId = new DeviceId(new UUID(deviceRpcCallMsg.getDeviceIdMSB(), deviceRpcCallMsg.getDeviceIdLSB())); |
||||
|
|
||||
|
FromDeviceRpcResponse response; |
||||
|
if (!StringUtils.isEmpty(deviceRpcCallMsg.getResponseMsg().getError())) { |
||||
|
response = new FromDeviceRpcResponse(requestUuid, null, RpcError.valueOf(deviceRpcCallMsg.getResponseMsg().getError())); |
||||
|
} else { |
||||
|
response = new FromDeviceRpcResponse(requestUuid, deviceRpcCallMsg.getResponseMsg().getResponse(), null); |
||||
|
} |
||||
|
TbQueueCallback callback = new TbQueueCallback() { |
||||
|
@Override |
||||
|
public void onSuccess(TbQueueMsgMetadata metadata) { |
||||
|
futureToSet.set(null); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.error("Can't process push notification to core [{}]", deviceRpcCallMsg, t); |
||||
|
futureToSet.setException(t); |
||||
|
} |
||||
|
}; |
||||
|
FromDeviceRpcResponseActorMsg msg = |
||||
|
new FromDeviceRpcResponseActorMsg(deviceRpcCallMsg.getRequestId(), |
||||
|
tenantId, |
||||
|
deviceId, response); |
||||
|
tbClusterService.pushMsgToCore(msg, callback); |
||||
|
return futureToSet; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,104 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.processor; |
||||
|
|
||||
|
import com.google.common.util.concurrent.Futures; |
||||
|
import com.google.common.util.concurrent.ListenableFuture; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.EntityType; |
||||
|
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
||||
|
import org.thingsboard.server.common.data.exception.ThingsboardException; |
||||
|
import org.thingsboard.server.common.data.id.AssetId; |
||||
|
import org.thingsboard.server.common.data.id.CustomerId; |
||||
|
import org.thingsboard.server.common.data.id.DashboardId; |
||||
|
import org.thingsboard.server.common.data.id.DeviceId; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
import org.thingsboard.server.common.data.id.EntityIdFactory; |
||||
|
import org.thingsboard.server.common.data.id.EntityViewId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.id.UserId; |
||||
|
import org.thingsboard.server.common.data.relation.EntityRelation; |
||||
|
import org.thingsboard.server.common.data.relation.RelationTypeGroup; |
||||
|
import org.thingsboard.server.gen.edge.RelationUpdateMsg; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
import java.util.UUID; |
||||
|
|
||||
|
@Component |
||||
|
@Slf4j |
||||
|
@TbCoreComponent |
||||
|
public class RelationProcessor extends BaseProcessor { |
||||
|
|
||||
|
public ListenableFuture<Void> onRelationUpdate(TenantId tenantId, RelationUpdateMsg relationUpdateMsg) { |
||||
|
log.trace("[{}] onRelationUpdate [{}]", tenantId, relationUpdateMsg); |
||||
|
try { |
||||
|
EntityRelation entityRelation = new EntityRelation(); |
||||
|
|
||||
|
UUID fromUUID = new UUID(relationUpdateMsg.getFromIdMSB(), relationUpdateMsg.getFromIdLSB()); |
||||
|
EntityId fromId = EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(relationUpdateMsg.getFromEntityType()), fromUUID); |
||||
|
entityRelation.setFrom(fromId); |
||||
|
|
||||
|
UUID toUUID = new UUID(relationUpdateMsg.getToIdMSB(), relationUpdateMsg.getToIdLSB()); |
||||
|
EntityId toId = EntityIdFactory.getByTypeAndUuid(EntityType.valueOf(relationUpdateMsg.getToEntityType()), toUUID); |
||||
|
entityRelation.setTo(toId); |
||||
|
|
||||
|
entityRelation.setType(relationUpdateMsg.getType()); |
||||
|
entityRelation.setTypeGroup(RelationTypeGroup.valueOf(relationUpdateMsg.getTypeGroup())); |
||||
|
entityRelation.setAdditionalInfo(mapper.readTree(relationUpdateMsg.getAdditionalInfo())); |
||||
|
switch (relationUpdateMsg.getMsgType()) { |
||||
|
case ENTITY_CREATED_RPC_MESSAGE: |
||||
|
case ENTITY_UPDATED_RPC_MESSAGE: |
||||
|
if (isEntityExists(tenantId, entityRelation.getTo()) |
||||
|
&& isEntityExists(tenantId, entityRelation.getFrom())) { |
||||
|
relationService.saveRelationAsync(tenantId, entityRelation); |
||||
|
} |
||||
|
break; |
||||
|
case ENTITY_DELETED_RPC_MESSAGE: |
||||
|
relationService.deleteRelation(tenantId, entityRelation); |
||||
|
break; |
||||
|
case UNRECOGNIZED: |
||||
|
log.error("Unsupported msg type"); |
||||
|
} |
||||
|
return Futures.immediateFuture(null); |
||||
|
} catch (Exception e) { |
||||
|
log.error("Failed to process relation update msg [{}]", relationUpdateMsg, e); |
||||
|
return Futures.immediateFailedFuture(new RuntimeException("Failed to process relation update msg", e)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
private boolean isEntityExists(TenantId tenantId, EntityId entityId) throws ThingsboardException { |
||||
|
switch (entityId.getEntityType()) { |
||||
|
case DEVICE: |
||||
|
return deviceService.findDeviceById(tenantId, new DeviceId(entityId.getId())) != null; |
||||
|
case ASSET: |
||||
|
return assetService.findAssetById(tenantId, new AssetId(entityId.getId())) != null; |
||||
|
case ENTITY_VIEW: |
||||
|
return entityViewService.findEntityViewById(tenantId, new EntityViewId(entityId.getId())) != null; |
||||
|
case CUSTOMER: |
||||
|
return customerService.findCustomerById(tenantId, new CustomerId(entityId.getId())) != null; |
||||
|
case USER: |
||||
|
return userService.findUserById(tenantId, new UserId(entityId.getId())) != null; |
||||
|
case DASHBOARD: |
||||
|
return dashboardService.findDashboardById(tenantId, new DashboardId(entityId.getId())) != null; |
||||
|
default: |
||||
|
throw new ThingsboardException("Unsupported entity type " + entityId.getEntityType(), ThingsboardErrorCode.INVALID_ARGUMENTS); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,282 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.edge.rpc.processor; |
||||
|
|
||||
|
import com.google.common.util.concurrent.FutureCallback; |
||||
|
import com.google.common.util.concurrent.Futures; |
||||
|
import com.google.common.util.concurrent.ListenableFuture; |
||||
|
import com.google.common.util.concurrent.SettableFuture; |
||||
|
import com.google.gson.Gson; |
||||
|
import com.google.gson.JsonObject; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.lang3.tuple.ImmutablePair; |
||||
|
import org.apache.commons.lang3.tuple.Pair; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.rule.engine.api.msg.DeviceAttributesEventNotificationMsg; |
||||
|
import org.thingsboard.server.common.data.DataConstants; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.DeviceProfile; |
||||
|
import org.thingsboard.server.common.data.EntityType; |
||||
|
import org.thingsboard.server.common.data.EntityView; |
||||
|
import org.thingsboard.server.common.data.asset.Asset; |
||||
|
import org.thingsboard.server.common.data.id.AssetId; |
||||
|
import org.thingsboard.server.common.data.id.CustomerId; |
||||
|
import org.thingsboard.server.common.data.id.DashboardId; |
||||
|
import org.thingsboard.server.common.data.id.DeviceId; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
import org.thingsboard.server.common.data.id.EntityViewId; |
||||
|
import org.thingsboard.server.common.data.id.RuleChainId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.id.UserId; |
||||
|
import org.thingsboard.server.common.data.kv.AttributeKey; |
||||
|
import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
||||
|
import org.thingsboard.server.common.msg.TbMsg; |
||||
|
import org.thingsboard.server.common.msg.TbMsgMetaData; |
||||
|
import org.thingsboard.server.common.msg.queue.ServiceQueue; |
||||
|
import org.thingsboard.server.common.msg.session.SessionMsgType; |
||||
|
import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
||||
|
import org.thingsboard.server.common.transport.util.JsonUtils; |
||||
|
import org.thingsboard.server.gen.edge.AttributeDeleteMsg; |
||||
|
import org.thingsboard.server.gen.edge.EntityDataProto; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos; |
||||
|
import org.thingsboard.server.queue.TbQueueCallback; |
||||
|
import org.thingsboard.server.queue.TbQueueMsgMetadata; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
import javax.annotation.Nullable; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.HashSet; |
||||
|
import java.util.List; |
||||
|
import java.util.Set; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
@Component |
||||
|
@Slf4j |
||||
|
@TbCoreComponent |
||||
|
public class TelemetryProcessor extends BaseProcessor { |
||||
|
|
||||
|
private final Gson gson = new Gson(); |
||||
|
|
||||
|
public List<ListenableFuture<Void>> onTelemetryUpdate(TenantId tenantId, EntityDataProto entityData) { |
||||
|
log.trace("[{}] onTelemetryUpdate [{}]", tenantId, entityData); |
||||
|
List<ListenableFuture<Void>> result = new ArrayList<>(); |
||||
|
EntityId entityId = constructEntityId(entityData); |
||||
|
if ((entityData.hasPostAttributesMsg() || entityData.hasPostTelemetryMsg() || entityData.hasAttributesUpdatedMsg()) && entityId != null) { |
||||
|
// TODO: voba - in terms of performance we should not fetch device from DB by id
|
||||
|
// TbMsgMetaData metaData = constructBaseMsgMetadata(tenantId, entityId);
|
||||
|
TbMsgMetaData metaData = new TbMsgMetaData(); |
||||
|
metaData.putValue(DataConstants.MSG_SOURCE_KEY, DataConstants.EDGE_MSG_SOURCE); |
||||
|
if (entityData.hasPostAttributesMsg()) { |
||||
|
result.add(processPostAttributes(tenantId, entityId, entityData.getPostAttributesMsg(), metaData)); |
||||
|
} |
||||
|
if (entityData.hasAttributesUpdatedMsg()) { |
||||
|
metaData.putValue("scope", entityData.getPostAttributeScope()); |
||||
|
result.add(processAttributesUpdate(tenantId, entityId, entityData.getAttributesUpdatedMsg(), metaData)); |
||||
|
} |
||||
|
if (entityData.hasPostTelemetryMsg()) { |
||||
|
result.add(processPostTelemetry(tenantId, entityId, entityData.getPostTelemetryMsg(), metaData)); |
||||
|
} |
||||
|
} |
||||
|
if (entityData.hasAttributeDeleteMsg()) { |
||||
|
result.add(processAttributeDeleteMsg(tenantId, entityId, entityData.getAttributeDeleteMsg(), entityData.getEntityType())); |
||||
|
} |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
private TbMsgMetaData constructBaseMsgMetadata(TenantId tenantId, EntityId entityId) { |
||||
|
TbMsgMetaData metaData = new TbMsgMetaData(); |
||||
|
switch (entityId.getEntityType()) { |
||||
|
case DEVICE: |
||||
|
Device device = deviceService.findDeviceById(tenantId, new DeviceId(entityId.getId())); |
||||
|
if (device != null) { |
||||
|
metaData.putValue("deviceName", device.getName()); |
||||
|
metaData.putValue("deviceType", device.getType()); |
||||
|
} |
||||
|
break; |
||||
|
case ASSET: |
||||
|
Asset asset = assetService.findAssetById(tenantId, new AssetId(entityId.getId())); |
||||
|
if (asset != null) { |
||||
|
metaData.putValue("assetName", asset.getName()); |
||||
|
metaData.putValue("assetType", asset.getType()); |
||||
|
} |
||||
|
break; |
||||
|
case ENTITY_VIEW: |
||||
|
EntityView entityView = entityViewService.findEntityViewById(tenantId, new EntityViewId(entityId.getId())); |
||||
|
if (entityView != null) { |
||||
|
metaData.putValue("entityViewName", entityView.getName()); |
||||
|
metaData.putValue("entityViewType", entityView.getType()); |
||||
|
} |
||||
|
break; |
||||
|
default: |
||||
|
log.debug("Using empty metadata for entityId [{}]", entityId); |
||||
|
break; |
||||
|
} |
||||
|
return metaData; |
||||
|
} |
||||
|
|
||||
|
private Pair<String, RuleChainId> getDefaultQueueNameAndRuleChainId(TenantId tenantId, EntityId entityId) { |
||||
|
if (EntityType.DEVICE.equals(entityId.getEntityType())) { |
||||
|
DeviceProfile deviceProfile = deviceProfileCache.get(tenantId, new DeviceId(entityId.getId())); |
||||
|
RuleChainId ruleChainId; |
||||
|
String queueName; |
||||
|
|
||||
|
if (deviceProfile == null) { |
||||
|
log.warn("[{}] Device profile is null!", entityId); |
||||
|
ruleChainId = null; |
||||
|
queueName = ServiceQueue.MAIN; |
||||
|
} else { |
||||
|
ruleChainId = deviceProfile.getDefaultRuleChainId(); |
||||
|
String defaultQueueName = deviceProfile.getDefaultQueueName(); |
||||
|
queueName = defaultQueueName != null ? defaultQueueName : ServiceQueue.MAIN; |
||||
|
} |
||||
|
return new ImmutablePair<>(queueName, ruleChainId); |
||||
|
} else { |
||||
|
return new ImmutablePair<>(ServiceQueue.MAIN, null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private ListenableFuture<Void> processPostTelemetry(TenantId tenantId, EntityId entityId, TransportProtos.PostTelemetryMsg msg, TbMsgMetaData metaData) { |
||||
|
SettableFuture<Void> futureToSet = SettableFuture.create(); |
||||
|
for (TransportProtos.TsKvListProto tsKv : msg.getTsKvListList()) { |
||||
|
JsonObject json = JsonUtils.getJsonObject(tsKv.getKvList()); |
||||
|
metaData.putValue("ts", tsKv.getTs() + ""); |
||||
|
Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); |
||||
|
String queueName = defaultQueueAndRuleChain.getKey(); |
||||
|
RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue(); |
||||
|
TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_TELEMETRY_REQUEST.name(), entityId, metaData, gson.toJson(json), ruleChainId, null); |
||||
|
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { |
||||
|
@Override |
||||
|
public void onSuccess(TbQueueMsgMetadata metadata) { |
||||
|
futureToSet.set(null); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.error("Can't process post telemetry [{}]", msg, t); |
||||
|
futureToSet.setException(t); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
return futureToSet; |
||||
|
} |
||||
|
|
||||
|
private ListenableFuture<Void> processPostAttributes(TenantId tenantId, EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) { |
||||
|
SettableFuture<Void> futureToSet = SettableFuture.create(); |
||||
|
JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); |
||||
|
Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); |
||||
|
String queueName = defaultQueueAndRuleChain.getKey(); |
||||
|
RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue(); |
||||
|
TbMsg tbMsg = TbMsg.newMsg(queueName, SessionMsgType.POST_ATTRIBUTES_REQUEST.name(), entityId, metaData, gson.toJson(json), ruleChainId, null); |
||||
|
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { |
||||
|
@Override |
||||
|
public void onSuccess(TbQueueMsgMetadata metadata) { |
||||
|
futureToSet.set(null); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.error("Can't process post attributes [{}]", msg, t); |
||||
|
futureToSet.setException(t); |
||||
|
} |
||||
|
}); |
||||
|
return futureToSet; |
||||
|
} |
||||
|
|
||||
|
private ListenableFuture<Void> processAttributesUpdate(TenantId tenantId, EntityId entityId, TransportProtos.PostAttributeMsg msg, TbMsgMetaData metaData) { |
||||
|
SettableFuture<Void> futureToSet = SettableFuture.create(); |
||||
|
JsonObject json = JsonUtils.getJsonObject(msg.getKvList()); |
||||
|
Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(json); |
||||
|
ListenableFuture<List<Void>> future = attributesService.save(tenantId, entityId, metaData.getValue("scope"), new ArrayList<>(attributes)); |
||||
|
Futures.addCallback(future, new FutureCallback<List<Void>>() { |
||||
|
@Override |
||||
|
public void onSuccess(@Nullable List<Void> voids) { |
||||
|
Pair<String, RuleChainId> defaultQueueAndRuleChain = getDefaultQueueNameAndRuleChainId(tenantId, entityId); |
||||
|
String queueName = defaultQueueAndRuleChain.getKey(); |
||||
|
RuleChainId ruleChainId = defaultQueueAndRuleChain.getValue(); |
||||
|
TbMsg tbMsg = TbMsg.newMsg(queueName, DataConstants.ATTRIBUTES_UPDATED, entityId, metaData, gson.toJson(json), ruleChainId, null); |
||||
|
tbClusterService.pushMsgToRuleEngine(tenantId, tbMsg.getOriginator(), tbMsg, new TbQueueCallback() { |
||||
|
@Override |
||||
|
public void onSuccess(TbQueueMsgMetadata metadata) { |
||||
|
futureToSet.set(null); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.error("Can't process attributes update [{}]", msg, t); |
||||
|
futureToSet.setException(t); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.error("Can't process attributes update [{}]", msg, t); |
||||
|
futureToSet.setException(t); |
||||
|
} |
||||
|
}, dbCallbackExecutorService); |
||||
|
return futureToSet; |
||||
|
} |
||||
|
|
||||
|
private ListenableFuture<Void> processAttributeDeleteMsg(TenantId tenantId, EntityId entityId, AttributeDeleteMsg attributeDeleteMsg, String entityType) { |
||||
|
SettableFuture<Void> futureToSet = SettableFuture.create(); |
||||
|
String scope = attributeDeleteMsg.getScope(); |
||||
|
List<String> attributeNames = attributeDeleteMsg.getAttributeNamesList(); |
||||
|
attributesService.removeAll(tenantId, entityId, scope, attributeNames); |
||||
|
if (EntityType.DEVICE.name().equals(entityType)) { |
||||
|
Set<AttributeKey> attributeKeys = new HashSet<>(); |
||||
|
for (String attributeName : attributeNames) { |
||||
|
attributeKeys.add(new AttributeKey(scope, attributeName)); |
||||
|
} |
||||
|
tbClusterService.pushMsgToCore(DeviceAttributesEventNotificationMsg.onDelete( |
||||
|
tenantId, (DeviceId) entityId, attributeKeys), new TbQueueCallback() { |
||||
|
@Override |
||||
|
public void onSuccess(TbQueueMsgMetadata metadata) { |
||||
|
futureToSet.set(null); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void onFailure(Throwable t) { |
||||
|
log.error("Can't process attribute delete msg [{}]", attributeDeleteMsg, t); |
||||
|
futureToSet.setException(t); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
return futureToSet; |
||||
|
} |
||||
|
|
||||
|
private EntityId constructEntityId(EntityDataProto entityData) { |
||||
|
EntityType entityType = EntityType.valueOf(entityData.getEntityType()); |
||||
|
switch (entityType) { |
||||
|
case DEVICE: |
||||
|
return new DeviceId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); |
||||
|
case ASSET: |
||||
|
return new AssetId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); |
||||
|
case ENTITY_VIEW: |
||||
|
return new EntityViewId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); |
||||
|
case DASHBOARD: |
||||
|
return new DashboardId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); |
||||
|
case TENANT: |
||||
|
return new TenantId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); |
||||
|
case CUSTOMER: |
||||
|
return new CustomerId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); |
||||
|
case USER: |
||||
|
return new UserId(new UUID(entityData.getEntityIdMSB(), entityData.getEntityIdLSB())); |
||||
|
default: |
||||
|
log.warn("Unsupported entity type [{}] during construct of entity id. EntityDataProto [{}]", entityData.getEntityType(), entityData); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,44 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.rpc; |
||||
|
|
||||
|
import lombok.Getter; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.ToString; |
||||
|
import org.thingsboard.rule.engine.api.msg.ToDeviceActorNotificationMsg; |
||||
|
import org.thingsboard.server.common.data.id.DeviceId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.msg.MsgType; |
||||
|
|
||||
|
@ToString |
||||
|
@RequiredArgsConstructor |
||||
|
public class FromDeviceRpcResponseActorMsg implements ToDeviceActorNotificationMsg { |
||||
|
|
||||
|
@Getter |
||||
|
private final Integer requestId; |
||||
|
@Getter |
||||
|
private final TenantId tenantId; |
||||
|
@Getter |
||||
|
private final DeviceId deviceId; |
||||
|
|
||||
|
@Getter |
||||
|
private final FromDeviceRpcResponse msg; |
||||
|
|
||||
|
@Override |
||||
|
public MsgType getMsgType() { |
||||
|
return MsgType.DEVICE_RPC_RESPONSE_TO_DEVICE_ACTOR_MSG; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,56 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2021 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.service.ttl.edge; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Value; |
||||
|
import org.springframework.scheduling.annotation.Scheduled; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.server.dao.util.PsqlDao; |
||||
|
import org.thingsboard.server.service.ttl.AbstractCleanUpService; |
||||
|
|
||||
|
import java.sql.Connection; |
||||
|
import java.sql.DriverManager; |
||||
|
import java.sql.SQLException; |
||||
|
|
||||
|
@PsqlDao |
||||
|
@Slf4j |
||||
|
@Service |
||||
|
public class EdgeEventsCleanUpService extends AbstractCleanUpService { |
||||
|
|
||||
|
@Value("${sql.ttl.edge_events.edge_events_ttl}") |
||||
|
private long ttl; |
||||
|
|
||||
|
@Value("${sql.ttl.edge_events.enabled}") |
||||
|
private boolean ttlTaskExecutionEnabled; |
||||
|
|
||||
|
@Scheduled(initialDelayString = "${sql.ttl.edge_events.execution_interval_ms}", fixedDelayString = "${sql.ttl.edge_events.execution_interval_ms}") |
||||
|
public void cleanUp() { |
||||
|
if (ttlTaskExecutionEnabled) { |
||||
|
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { |
||||
|
doCleanUp(conn); |
||||
|
} catch (SQLException e) { |
||||
|
log.error("SQLException occurred during TTL task execution ", e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void doCleanUp(Connection connection) throws SQLException { |
||||
|
long totalEdgeEventsRemoved = executeQuery(connection, "call cleanup_edge_events_by_ttl(" + ttl + ", 0);"); |
||||
|
log.info("Total edge events removed by TTL: [{}]", totalEdgeEventsRemoved); |
||||
|
} |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue