committed by
GitHub
484 changed files with 26472 additions and 1487 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