diff --git a/.github/ISSUE_TEMPLATE/---bug-report.md b/.github/ISSUE_TEMPLATE/---bug-report.md
index 6d8889f98d..f5d093f424 100644
--- a/.github/ISSUE_TEMPLATE/---bug-report.md
+++ b/.github/ISSUE_TEMPLATE/---bug-report.md
@@ -3,7 +3,7 @@ name: "\U0001F41E Bug report"
about: Create a report to help us improve
title: "Your title here"
labels: ['bug', 'unconfirmed']
-assignees: AndriichnekoDm
+assignees: Ultrazombie
---
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index c73f810cd8..1168adaf15 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -3,7 +3,7 @@ name: Feature request
about: Suggest an idea for this project
title: "Your title here"
labels: ['feature']
-assignees: 'AndriichnekoDm'
+assignees: 'Ultrazombie'
---
diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md
index 2f15690f68..dea6318ac5 100644
--- a/.github/ISSUE_TEMPLATE/question.md
+++ b/.github/ISSUE_TEMPLATE/question.md
@@ -3,7 +3,7 @@ name: Question
about: Describe your questions in detail
title: "Your title here"
labels: ['question']
-assignees: 'AndriichnekoDm'
+assignees: 'Ultrazombie'
---
diff --git a/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json b/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json
index 83a96c6fe6..99ff00e602 100644
--- a/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json
+++ b/application/src/main/data/json/demo/dashboards/rule_engine_statistics.json
@@ -13,6 +13,7 @@
"datasources": [
{
"type": "entity",
+ "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018",
"dataKeys": [
{
"name": "ruleEngineException",
@@ -51,7 +52,59 @@
"_hash": 0.7255162989552142
}
],
- "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018"
+ "alarmFilterConfig": {
+ "statusList": [
+ "ACTIVE"
+ ]
+ },
+ "latestDataKeys": [
+ {
+ "name": "queueName",
+ "type": "entityField",
+ "label": "Queue name",
+ "color": "#ffc107",
+ "settings": {
+ "show": false,
+ "order": null,
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.8104572478982748,
+ "aggregationType": null,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ },
+ {
+ "name": "serviceId",
+ "type": "entityField",
+ "label": "Service Id",
+ "color": "#607d8b",
+ "settings": {
+ "show": false,
+ "order": null,
+ "useCellStyleFunction": false,
+ "cellStyleFunction": "",
+ "useCellContentFunction": false,
+ "cellContentFunction": "",
+ "defaultColumnVisibility": "visible",
+ "columnSelectionToDisplay": "enabled"
+ },
+ "_hash": 0.38329217099945034,
+ "aggregationType": null,
+ "units": null,
+ "decimals": null,
+ "funcBody": null,
+ "usePostProcessing": null,
+ "postFuncBody": null
+ }
+ ]
}
],
"timewindow": {
@@ -71,7 +124,9 @@
"settings": {
"showTimestamp": true,
"displayPagination": true,
- "defaultPageSize": 10
+ "defaultPageSize": 10,
+ "enableSearch": true,
+ "enableSelectColumnDisplay": true
},
"title": "Exceptions",
"dropShadow": true,
@@ -89,7 +144,10 @@
"iconColor": "rgba(0, 0, 0, 0.87)",
"iconSize": "24px",
"titleTooltip": "",
- "displayTimewindow": true
+ "displayTimewindow": true,
+ "configMode": "basic",
+ "titleFont": null,
+ "titleColor": null
},
"id": "5eb79712-5c24-3060-7e4f-6af36b8f842d",
"typeFullFqn": "system.cards.timeseries_table"
@@ -329,7 +387,25 @@
"statusList": [
"ACTIVE"
]
- }
+ },
+ "latestDataKeys": [
+ {
+ "name": "queueName",
+ "type": "entityField",
+ "label": "Queue name",
+ "color": "#ffc107",
+ "settings": {},
+ "_hash": 0.8012481564934415
+ },
+ {
+ "name": "serviceId",
+ "type": "entityField",
+ "label": "Service Id",
+ "color": "#607d8b",
+ "settings": {},
+ "_hash": 0.0724871638610094
+ }
+ ]
}
],
"timewindow": {
@@ -724,7 +800,25 @@
"statusList": [
"ACTIVE"
]
- }
+ },
+ "latestDataKeys": [
+ {
+ "name": "queueName",
+ "type": "entityField",
+ "label": "Queue name",
+ "color": "#f44336",
+ "settings": {},
+ "_hash": 0.7242351292118758
+ },
+ {
+ "name": "serviceId",
+ "type": "entityField",
+ "label": "Service Id",
+ "color": "#ffc107",
+ "settings": {},
+ "_hash": 0.3347262075244206
+ }
+ ]
}
],
"timewindow": {
@@ -1004,12 +1098,9 @@
"id": "140f23dd-e3a0-ed98-6189-03c49d2d8018",
"alias": "TbServiceQueues",
"filter": {
- "type": "assetType",
+ "type": "entityType",
"resolveMultiple": true,
- "assetNameFilter": "",
- "assetTypes": [
- "TbServiceQueue"
- ]
+ "entityType": "QUEUE_STATS"
}
}
},
diff --git a/application/src/main/data/json/system/widget_bundles/charts.json b/application/src/main/data/json/system/widget_bundles/charts.json
index 979b241e67..a66975d1e2 100644
--- a/application/src/main/data/json/system/widget_bundles/charts.json
+++ b/application/src/main/data/json/system/widget_bundles/charts.json
@@ -12,10 +12,11 @@
"line_chart",
"bar_chart",
"point_chart",
+ "state_chart",
+ "bar_chart_with_labels",
+ "range_chart",
"charts.basic_timeseries",
"charts.state_chart",
- "range_chart",
- "bar_chart_with_labels",
"charts.timeseries_bars_flot",
"cards.aggregated_value_card",
"charts.bars",
diff --git a/application/src/main/data/json/system/widget_types/state_chart.json b/application/src/main/data/json/system/widget_types/state_chart.json
index a78fc06f64..0af2631813 100644
--- a/application/src/main/data/json/system/widget_types/state_chart.json
+++ b/application/src/main/data/json/system/widget_types/state_chart.json
@@ -1,26 +1,34 @@
{
- "fqn": "charts.state_chart",
- "name": "State Chart",
+ "fqn": "state_chart",
+ "name": "State chart",
"deprecated": false,
- "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAB9VBMVEUAAAAhlvMilvMymeE+nNVDoetInslNq/VZqOdpuPd3d3d5p5Z5suB6enp8fHyBgYGDg4ODqoqEhISIiIiKioqKuN2MjIyNjY2Ojo6QkJCRkZGSkpKUlJSVlZWWlpaXl5eYmJiZmZmampqcnJydnZ2enp6goKCgr3KhoaGioqKisXSjo6OjvsmkpKSlpaWlwdempqanp6eoqKiosGOo1vqpqampsGOqqqqqsWOrq6usrKysw9Wurq6wsLCysrK0tLS1tbW1wbK2tra3t7e4wau5ubm6urq7u7u8vLy9vb2+vr6/xbTAwMDAxbjCwsLC3ejDw8PExMTFxcXGxsbHx8fIv3jIyMjJycnKysrK5vzMzc7Nzc3Ozs7Pz8/QuDnRzcHS0tLT09PT39HU1NTU6/3VzLLV1dXV3sjW1tbX19fYuTHY2NjZ2dna0Ira2trb29vc3Nzex4De3t7f39/guyvhvCfhvCvh4eHh6Nbi4uLjvCTj4+PkyXbk5OTlvCTm5ubm693o6Ojpxl7q6urr6+vswTTs7Ozt7e3uvhju7u7vvhjv7+/wvxjw8PDx8fHyvxX0yTv09PT19fX29vb39/f4wyL4+Pj5+fn6+vr75J37+/v8whP8/Pz9/f39/v/+/v7/wQf/xRb/3HT/5JH/9tz/++/////APs7XAAAAAWJLR0Smt7AblQAAA1RJREFUeNrt3dlT01AUBvAEd8UNl2oLrdrFolZArUul1hWlVhQXFAUF1xaxIqi4FUQUrDsUClZi4nb+Th96S9NQkjDOOKZ+3wuZw8md++M25OXMlKOccJQnTUYocdRqdw8UAqTfOjG4lvr7uowOqbtAtG6o5OiGFoNDapuJHO8tFK4xOKS9msTVgoUiaQjPclnSl01snZ/y4ly2yBNZbRarbc+3yvdpt/hLawOPJp9ur7O0mRiEmzFkY1M6N/4I8qJpulzR2sBx1sgRjQuyA2pk+STdylw2VqR/FPFfWdcCvjhduiM7kVF2db1Nuspu7JGes+I76SRba7f0Wfnn/6F6It+mfo7m8TvYvh7KTiT3PQIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggPwXkFa7e7AQIP3WiUFzdl7LuBDFvJZxIYp5LeNCFPNaBn7Yc+e1KlheSYcrFCniL7LZqPl8cbp0TjavNcqu9rZJB9gdPdIJVnwjbWG1ndI95UzWM9V5rS9Ti3P4VWy1+5jXAgQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAMK+FeS3Ma2FeC/NamNfCCxEQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBA/hZkTKSkwSGn7VUJOmOLV241NiRSRZ2uYcvt+Io+Y0N83USm+MqGWEnU2JCqXqI1ZE+QhYiIy36tXR5INpOQbB5kftcmK85mtdey2iJekeWqX6/3kc+TLCQTrq6eEuZCgKTsbnNXBsIZOERJMedNQnry73XpW8UAKVhIyBPScVfQE9RqEaP7iMT6g+pdw7vKbxKl3IJqV8K7OUqXPHvG9UMGykk2lz1d4g5yvNXo6QqZiA41aHT5OwQTUWBJSrWrvlMwj1jFU2f1Q8K1dCysCUmVhdenNLvMRKZt3hGNriEnxfxlGqt9aPYRkb9bP6Q1SMFrmltMukKuMT2QpckWjc/WhP2lYB/TgjzdHyCKVM/gGXnsp+qY5hbba+hIVA/ETL0+9SepsoNiTs/igGpXZIhKqVv9QVJARIfXIWqfiM1nS+qBnHdae1V7Qss8ngEijRO5a6sMCAtdqv+HfgPwpNPbU6ipOwAAAABJRU5ErkJggg==",
+ "image": "tb-image:Y2hhcnRfKDUpLnN2Zw==:IlN0YXRlIGNoYXJ0IiBzeXN0ZW0gd2lkZ2V0IGltYWdl;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF80NDM3Xzk2ODU4KSI+CjxwYXRoIGQ9Ik0yLjc2NTYyIDEuODE2NDFMMC44ODI4MTIgN0gwLjExMzI4MUwyLjI4MTI1IDEuMzEyNUgyLjc3NzM0TDIuNzY1NjIgMS44MTY0MVpNNC4zNDM3NSA3TDIuNDU3MDMgMS44MTY0MUwyLjQ0NTMxIDEuMzEyNUgyLjk0MTQxTDUuMTE3MTkgN0g0LjM0Mzc1Wk00LjI0NjA5IDQuODk0NTNWNS41MTE3MkgxLjA1MDc4VjQuODk0NTNINC4yNDYwOVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPHBhdGggZD0iTTMuNjg3NSAzMi41NDY0SDIuMjQ2MDlMMi4yMzgyOCAzMS45NDA5SDMuNTQ2ODhDMy43NjMwMiAzMS45NDA5IDMuOTUxODIgMzEuOTA0NSA0LjExMzI4IDMxLjgzMTVDNC4yNzQ3NCAzMS43NTg2IDQuMzk5NzQgMzEuNjU0NSA0LjQ4ODI4IDMxLjUxOUM0LjU3OTQzIDMxLjM4MSA0LjYyNSAzMS4yMTcgNC42MjUgMzEuMDI2OUM0LjYyNSAzMC44MTg1IDQuNTg0NjQgMzAuNjQ5MyA0LjUwMzkxIDMwLjUxOUM0LjQyNTc4IDMwLjM4NjIgNC4zMDQ2OSAzMC4yODk5IDQuMTQwNjIgMzAuMjNDMy45NzkxNyAzMC4xNjc1IDMuNzczNDQgMzAuMTM2MiAzLjUyMzQ0IDMwLjEzNjJIMi40MTQwNlYzNS4yMDY1SDEuNjYwMTZWMjkuNTE5SDMuNTIzNDRDMy44MTUxIDI5LjUxOSA0LjA3NTUyIDI5LjU0OSA0LjMwNDY5IDI5LjYwODlDNC41MzM4NSAyOS42NjYyIDQuNzI3ODYgMjkuNzU3MyA0Ljg4NjcyIDI5Ljg4MjNDNS4wNDgxOCAzMC4wMDQ3IDUuMTcwNTcgMzAuMTYxIDUuMjUzOTEgMzAuMzUxMUM1LjMzNzI0IDMwLjU0MTIgNS4zNzg5MSAzMC43NjkgNS4zNzg5MSAzMS4wMzQ3QzUuMzc4OTEgMzEuMjY5IDUuMzE5MDEgMzEuNDgxMyA1LjE5OTIyIDMxLjY3MTRDNS4wNzk0MyAzMS44NTg5IDQuOTEyNzYgMzIuMDEyNSA0LjY5OTIyIDMyLjEzMjNDNC40ODgyOCAzMi4yNTIxIDQuMjQwODkgMzIuMzI4OSAzLjk1NzAzIDMyLjM2MjhMMy42ODc1IDMyLjU0NjRaTTMuNjUyMzQgMzUuMjA2NUgxLjk0OTIyTDIuMzc1IDM0LjU5MzNIMy42NTIzNEMzLjg5MTkzIDM0LjU5MzMgNC4wOTUwNSAzNC41NTE2IDQuMjYxNzIgMzQuNDY4M0M0LjQzMDk5IDM0LjM4NDkgNC41NTk5IDM0LjI2NzcgNC42NDg0NCAzNC4xMTY3QzQuNzM2OTggMzMuOTYzMSA0Ljc4MTI1IDMzLjc4MjEgNC43ODEyNSAzMy41NzM3QzQuNzgxMjUgMzMuMzYyOCA0Ljc0MzQ5IDMzLjE4MDUgNC42Njc5NyAzMy4wMjY5QzQuNTkyNDUgMzIuODczMiA0LjQ3Mzk2IDMyLjc1NDcgNC4zMTI1IDMyLjY3MTRDNC4xNTEwNCAzMi41ODgxIDMuOTQyNzEgMzIuNTQ2NCAzLjY4NzUgMzIuNTQ2NEgyLjYxMzI4TDIuNjIxMDkgMzEuOTQwOUg0LjA4OTg0TDQuMjUgMzIuMTU5N0M0LjUyMzQ0IDMyLjE4MzEgNC43NTUyMSAzMi4yNjEyIDQuOTQ1MzEgMzIuMzk0QzUuMTM1NDIgMzIuNTI0MyA1LjI3OTk1IDMyLjY5MDkgNS4zNzg5MSAzMi44OTRDNS40ODA0NyAzMy4wOTcyIDUuNTMxMjUgMzMuMzIxMSA1LjUzMTI1IDMzLjU2NTlDNS41MzEyNSAzMy45MjAxIDUuNDUzMTIgMzQuMjE5NiA1LjI5Njg4IDM0LjQ2NDRDNS4xNDMyMyAzNC43MDY1IDQuOTI1NzggMzQuODkxNCA0LjY0NDUzIDM1LjAxOUM0LjM2MzI4IDM1LjE0NCA0LjAzMjU1IDM1LjIwNjUgMy42NTIzNCAzNS4yMDY1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNNC4wOTM3NSA2MS42MDVINC44NDM3NUM0LjgwNDY5IDYxLjk2NDQgNC43MDE4MiA2Mi4yODYgNC41MzUxNiA2Mi41Njk4QzQuMzY4NDkgNjIuODUzNyA0LjEzMjgxIDYzLjA3ODkgMy44MjgxMiA2My4yNDU2QzMuNTIzNDQgNjMuNDA5NyAzLjE0MzIzIDYzLjQ5MTcgMi42ODc1IDYzLjQ5MTdDMi4zNTQxNyA2My40OTE3IDIuMDUwNzggNjMuNDI5MiAxLjc3NzM0IDYzLjMwNDJDMS41MDY1MSA2My4xNzkyIDEuMjczNDQgNjMuMDAyMSAxLjA3ODEyIDYyLjc3MjlDMC44ODI4MTIgNjIuNTQxMiAwLjczMTc3MSA2Mi4yNjM4IDAuNjI1IDYxLjk0MDlDMC41MjA4MzMgNjEuNjE1NCAwLjQ2ODc1IDYxLjI1MzQgMC40Njg3NSA2MC44NTVWNjAuMjg4NkMwLjQ2ODc1IDU5Ljg5MDEgMC41MjA4MzMgNTkuNTI5NSAwLjYyNSA1OS4yMDY1QzAuNzMxNzcxIDU4Ljg4MSAwLjg4NDExNSA1OC42MDI0IDEuMDgyMDMgNTguMzcwNkMxLjI4MjU1IDU4LjEzODggMS41MjM0NCA1Ny45NjA0IDEuODA0NjkgNTcuODM1NEMyLjA4NTk0IDU3LjcxMDQgMi40MDIzNCA1Ny42NDc5IDIuNzUzOTEgNTcuNjQ3OUMzLjE4MzU5IDU3LjY0NzkgMy41NDY4OCA1Ny43Mjg3IDMuODQzNzUgNTcuODkwMUM0LjE0MDYyIDU4LjA1MTYgNC4zNzEwOSA1OC4yNzU2IDQuNTM1MTYgNTguNTYyQzQuNzAxODIgNTguODQ1OSA0LjgwNDY5IDU5LjE3NTMgNC44NDM3NSA1OS41NTAzSDQuMDkzNzVDNC4wNTcyOSA1OS4yODQ3IDMuOTg5NTggNTkuMDU2OCAzLjg5MDYyIDU4Ljg2NjdDMy43OTE2NyA1OC42NzQgMy42NTEwNCA1OC41MjU2IDMuNDY4NzUgNTguNDIxNEMzLjI4NjQ2IDU4LjMxNzIgMy4wNDgxOCA1OC4yNjUxIDIuNzUzOTEgNTguMjY1MUMyLjUwMTMgNTguMjY1MSAyLjI3ODY1IDU4LjMxMzMgMi4wODU5NCA1OC40MDk3QzEuODk1ODMgNTguNTA2IDEuNzM1NjggNTguNjQyNyAxLjYwNTQ3IDU4LjgxOThDMS40Nzc4NiA1OC45OTY5IDEuMzgxNTEgNTkuMjA5MSAxLjMxNjQxIDU5LjQ1NjVDMS4yNTEzIDU5LjcwMzkgMS4yMTg3NSA1OS45Nzg3IDEuMjE4NzUgNjAuMjgwOFY2MC44NTVDMS4yMTg3NSA2MS4xMzM2IDEuMjQ3NCA2MS4zOTUzIDEuMzA0NjkgNjEuNjQwMUMxLjM2NDU4IDYxLjg4NDkgMS40NTQ0MyA2Mi4wOTk4IDEuNTc0MjIgNjIuMjg0N0MxLjY5NDAxIDYyLjQ2OTYgMS44NDYzNSA2Mi42MTU0IDIuMDMxMjUgNjIuNzIyMkMyLjIxNjE1IDYyLjgyNjMgMi40MzQ5IDYyLjg3ODQgMi42ODc1IDYyLjg3ODRDMy4wMDc4MSA2Mi44Nzg0IDMuMjYzMDIgNjIuODI3NiAzLjQ1MzEyIDYyLjcyNjFDMy42NDMyMyA2Mi42MjQ1IDMuNzg2NDYgNjIuNDc4NyAzLjg4MjgxIDYyLjI4ODZDMy45ODE3NyA2Mi4wOTg1IDQuMDUyMDggNjEuODcwNiA0LjA5Mzc1IDYxLjYwNVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPHBhdGggZD0iTTIuMTk5MjIgOTEuNjIwMUgxLjAxMTcyTDEuMDE5NTMgOTEuMDA2OEgyLjE5OTIyQzIuNjA1NDcgOTEuMDA2OCAyLjk0NDAxIDkwLjkyMjIgMy4yMTQ4NCA5MC43NTI5QzMuNDg1NjggOTAuNTgxMSAzLjY4ODggOTAuMzQxNSAzLjgyNDIyIDkwLjAzNDJDMy45NjIyNCA4OS43MjQzIDQuMDMxMjUgODkuMzYyMyA0LjAzMTI1IDg4Ljk0ODJWODguNjAwNkM0LjAzMTI1IDg4LjI3NTEgMy45OTIxOSA4Ny45ODYgMy45MTQwNiA4Ny43MzM0QzMuODM1OTQgODcuNDc4MiAzLjcyMTM1IDg3LjI2MzMgMy41NzAzMSA4Ny4wODg5QzMuNDE5MjcgODYuOTExOCAzLjIzNDM4IDg2Ljc3NzcgMy4wMTU2MiA4Ni42ODY1QzIuNzk5NDggODYuNTk1NCAyLjU1MDc4IDg2LjU0OTggMi4yNjk1MyA4Ni41NDk4SDAuOTg4MjgxVjg1LjkzMjZIMi4yNjk1M0MyLjY0MTkzIDg1LjkzMjYgMi45ODE3NyA4NS45OTUxIDMuMjg5MDYgODYuMTIwMUMzLjU5NjM1IDg2LjI0MjUgMy44NjA2OCA4Ni40MjA5IDQuMDgyMDMgODYuNjU1M0M0LjMwNTk5IDg2Ljg4NyA0LjQ3Nzg2IDg3LjE2ODMgNC41OTc2NiA4Ny40OTlDNC43MTc0NSA4Ny44MjcxIDQuNzc3MzQgODguMTk2OSA0Ljc3NzM0IDg4LjYwODRWODguOTQ4MkM0Ljc3NzM0IDg5LjM1OTcgNC43MTc0NSA4OS43MzA4IDQuNTk3NjYgOTAuMDYxNUM0LjQ3Nzg2IDkwLjM4OTYgNC4zMDQ2OSA5MC42Njk2IDQuMDc4MTIgOTAuOTAxNEMzLjg1NDE3IDkxLjEzMzEgMy41ODMzMyA5MS4zMTE1IDMuMjY1NjIgOTEuNDM2NUMyLjk1MDUyIDkxLjU1ODkgMi41OTUwNSA5MS42MjAxIDIuMTk5MjIgOTEuNjIwMVpNMS40MTQwNiA4NS45MzI2VjkxLjYyMDFIMC42NjAxNTZWODUuOTMyNkgxLjQxNDA2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNNS4yNzM0NCAxMTkuMjE0VjExOS44MjdIMi4yNjE3MlYxMTkuMjE0SDUuMjczNDRaTTIuNDE0MDYgMTE0LjE0VjExOS44MjdIMS42NjAxNlYxMTQuMTRIMi40MTQwNlpNNC44NzUgMTE2LjU4NVYxMTcuMTk4SDIuMjYxNzJWMTE2LjU4NUg0Ljg3NVpNNS4yMzQzOCAxMTQuMTRWMTE0Ljc1N0gyLjI2MTcyVjExNC4xNEg1LjIzNDM4WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNMTEgNC4xNjExM0wxODYgNC4xNjExNiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPHBhdGggZD0iTTExIDMzLjE2MTFMMTg2IDMzLjE2MTIiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjEyIi8+CjxwYXRoIGQ9Ik0xMSA2MS4xNjExTDE4NiA2MS4xNjEyIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMTEgODkuMTYxMUwxODYgODkuMTYxMiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPHBhdGggZD0iTTExIDExOC4xNjFMMTg2IDExOC4xNjEiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjEyIi8+CjxsaW5lIHgxPSI5LjIiIHkxPSIxNDUuOTYxIiB4Mj0iMTg4LjgiIHkyPSIxNDUuOTYxIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC43IiBzdHJva2Utd2lkdGg9IjAuNCIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8bGluZSB4MT0iMjYuNDUwMiIgeTE9IjE0OC4wNzIiIHgyPSIyNi40NTAyIiB5Mj0iMTQ3LjI1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNMTguNTA5MSAxNTIuMDI1VjE1Mi44OTNDMTguNTA5MSAxNTMuMzU5IDE4LjQ2NzQgMTUzLjc1MiAxOC4zODQxIDE1NC4wNzJDMTguMzAwNyAxNTQuMzkzIDE4LjE4MDkgMTU0LjY1IDE4LjAyNDcgMTU0Ljg0NkMxNy44Njg0IDE1NS4wNDEgMTcuNjc5NiAxNTUuMTgzIDE3LjQ1ODMgMTU1LjI3MUMxNy4yMzk1IDE1NS4zNTcgMTYuOTkyMSAxNTUuNCAxNi43MTYxIDE1NS40QzE2LjQ5NzMgMTU1LjQgMTYuMjk1NSAxNTUuMzczIDE2LjExMDYgMTU1LjMxOEMxNS45MjU3IDE1NS4yNjQgMTUuNzU5MSAxNTUuMTc2IDE1LjYxMDYgMTU1LjA1N0MxNS40NjQ4IDE1NC45MzQgMTUuMzM5OCAxNTQuNzc1IDE1LjIzNTYgMTU0LjU4QzE1LjEzMTUgMTU0LjM4NSAxNS4wNTIgMTU0LjE0OCAxNC45OTczIDE1My44NjlDMTQuOTQyNyAxNTMuNTkgMTQuOTE1MyAxNTMuMjY1IDE0LjkxNTMgMTUyLjg5M1YxNTIuMDI1QzE0LjkxNTMgMTUxLjU1OSAxNC45NTcgMTUxLjE2OSAxNS4wNDAzIDE1MC44NTRDMTUuMTI2MiAxNTAuNTM4IDE1LjI0NzMgMTUwLjI4NiAxNS40MDM2IDE1MC4wOTZDMTUuNTU5OCAxNDkuOTAzIDE1Ljc0NzMgMTQ5Ljc2NSAxNS45NjYxIDE0OS42ODJDMTYuMTg3NCAxNDkuNTk4IDE2LjQzNDggMTQ5LjU1NyAxNi43MDgzIDE0OS41NTdDMTYuOTI5NiAxNDkuNTU3IDE3LjEzMjggMTQ5LjU4NCAxNy4zMTc3IDE0OS42MzlDMTcuNTA1MiAxNDkuNjkxIDE3LjY3MTggMTQ5Ljc3NSAxNy44MTc3IDE0OS44OTNDMTcuOTYzNSAxNTAuMDA3IDE4LjA4NzIgMTUwLjE2MSAxOC4xODg3IDE1MC4zNTRDMTguMjkyOSAxNTAuNTQ0IDE4LjM3MjMgMTUwLjc3NyAxOC40MjcgMTUxLjA1M0MxOC40ODE3IDE1MS4zMjkgMTguNTA5MSAxNTEuNjUzIDE4LjUwOTEgMTUyLjAyNVpNMTcuNzgyNSAxNTMuMDFWMTUxLjkwNEMxNy43ODI1IDE1MS42NDkgMTcuNzY2OSAxNTEuNDI1IDE3LjczNTYgMTUxLjIzMkMxNy43MDcgMTUxLjAzNyAxNy42NjQgMTUwLjg3IDE3LjYwNjcgMTUwLjczMkMxNy41NDk0IDE1MC41OTQgMTcuNDc2NSAxNTAuNDgyIDE3LjM4OCAxNTAuMzk2QzE3LjMwMiAxNTAuMzExIDE3LjIwMTggMTUwLjI0OCAxNy4wODcyIDE1MC4yMDlDMTYuOTc1MiAxNTAuMTY3IDE2Ljg0ODkgMTUwLjE0NiAxNi43MDgzIDE1MC4xNDZDMTYuNTM2NCAxNTAuMTQ2IDE2LjM4NDEgMTUwLjE3OSAxNi4yNTEyIDE1MC4yNDRDMTYuMTE4NCAxNTAuMzA3IDE2LjAwNjUgMTUwLjQwNyAxNS45MTUzIDE1MC41NDVDMTUuODI2OCAxNTAuNjgzIDE1Ljc1OTEgMTUwLjg2NCAxNS43MTIyIDE1MS4wODhDMTUuNjY1MyAxNTEuMzEyIDE1LjY0MTkgMTUxLjU4NCAxNS42NDE5IDE1MS45MDRWMTUzLjAxQzE1LjY0MTkgMTUzLjI2NSAxNS42NTYyIDE1My40OSAxNS42ODQ4IDE1My42ODZDMTUuNzE2MSAxNTMuODgxIDE1Ljc2MTcgMTU0LjA1IDE1LjgyMTYgMTU0LjE5M0MxNS44ODE1IDE1NC4zMzQgMTUuOTU0NCAxNTQuNDUgMTYuMDQwMyAxNTQuNTQxQzE2LjEyNjIgMTU0LjYzMiAxNi4yMjUyIDE1NC43IDE2LjMzNzIgMTU0Ljc0NEMxNi40NTE4IDE1NC43ODYgMTYuNTc4MSAxNTQuODA3IDE2LjcxNjEgMTU0LjgwN0MxNi44OTMyIDE1NC44MDcgMTcuMDQ4MSAxNTQuNzczIDE3LjE4MDkgMTU0LjcwNUMxNy4zMTM3IDE1NC42MzcgMTcuNDI0NCAxNTQuNTMyIDE3LjUxMyAxNTQuMzg5QzE3LjYwNDEgMTU0LjI0MyAxNy42NzE4IDE1NC4wNTcgMTcuNzE2MSAxNTMuODNDMTcuNzYwNCAxNTMuNjAxIDE3Ljc4MjUgMTUzLjMyNyAxNy43ODI1IDE1My4wMVpNMjEuODk2NCAxNDkuNjA0VjE1NS4zMjJIMjEuMTczN1YxNTAuNTA2TDE5LjcxNjcgMTUxLjAzN1YxNTAuMzg1TDIxLjc4MzEgMTQ5LjYwNEgyMS44OTY0Wk0yNy4xMTI0IDE0OS42MzVWMTU1LjMyMkgyNi4zNTg1VjE0OS42MzVIMjcuMTEyNFpNMjkuNDk1MiAxNTIuMTkzVjE1Mi44MTFIMjYuOTQ4M1YxNTIuMTkzSDI5LjQ5NTJaTTI5Ljg4MTkgMTQ5LjYzNVYxNTAuMjUySDI2Ljk0ODNWMTQ5LjYzNUgyOS44ODE5Wk0zMi40MjE2IDE1NS40QzMyLjEyNzMgMTU1LjQgMzEuODYwNCAxNTUuMzUxIDMxLjYyMDggMTU1LjI1MkMzMS4zODM4IDE1NS4xNSAzMS4xNzk0IDE1NS4wMDggMzEuMDA3NSAxNTQuODI2QzMwLjgzODMgMTU0LjY0NCAzMC43MDgxIDE1NC40MjggMzAuNjE2OSAxNTQuMTc4QzMwLjUyNTggMTUzLjkyOCAzMC40ODAyIDE1My42NTQgMzAuNDgwMiAxNTMuMzU3VjE1My4xOTNDMzAuNDgwMiAxNTIuODUgMzAuNTMxIDE1Mi41NDQgMzAuNjMyNSAxNTIuMjc1QzMwLjczNDEgMTUyLjAwNSAzMC44NzIxIDE1MS43NzUgMzEuMDQ2NiAxNTEuNTg4QzMxLjIyMTEgMTUxLjQgMzEuNDE5IDE1MS4yNTggMzEuNjQwMyAxNTEuMTYyQzMxLjg2MTcgMTUxLjA2NiAzMi4wOTA5IDE1MS4wMTggMzIuMzI3OCAxNTEuMDE4QzMyLjYyOTkgMTUxLjAxOCAzMi44OTAzIDE1MS4wNyAzMy4xMDkxIDE1MS4xNzRDMzMuMzMwNSAxNTEuMjc4IDMzLjUxMTQgMTUxLjQyNCAzMy42NTIxIDE1MS42MTFDMzMuNzkyNyAxNTEuNzk2IDMzLjg5NjkgMTUyLjAxNSAzMy45NjQ2IDE1Mi4yNjhDMzQuMDMyMyAxNTIuNTE4IDM0LjA2NjEgMTUyLjc5MSAzNC4wNjYxIDE1My4wODhWMTUzLjQxMkgzMC45MDk5VjE1Mi44MjJIMzMuMzQzNVYxNTIuNzY4QzMzLjMzMzEgMTUyLjU4IDMzLjI5NCAxNTIuMzk4IDMzLjIyNjMgMTUyLjIyMUMzMy4xNjEyIDE1Mi4wNDQgMzMuMDU3IDE1MS44OTggMzIuOTEzOCAxNTEuNzgzQzMyLjc3MDYgMTUxLjY2OSAzMi41NzUyIDE1MS42MTEgMzIuMzI3OCAxNTEuNjExQzMyLjE2MzggMTUxLjYxMSAzMi4wMTI3IDE1MS42NDYgMzEuODc0NyAxNTEuNzE3QzMxLjczNjcgMTUxLjc4NSAzMS42MTgyIDE1MS44ODYgMzEuNTE5MyAxNTIuMDIxQzMxLjQyMDMgMTUyLjE1NyAzMS4zNDM1IDE1Mi4zMjIgMzEuMjg4OCAxNTIuNTE4QzMxLjIzNDEgMTUyLjcxMyAzMS4yMDY4IDE1Mi45MzggMzEuMjA2OCAxNTMuMTkzVjE1My4zNTdDMzEuMjA2OCAxNTMuNTU4IDMxLjIzNDEgMTUzLjc0NyAzMS4yODg4IDE1My45MjRDMzEuMzQ2MSAxNTQuMDk4IDMxLjQyODEgMTU0LjI1MiAzMS41MzQ5IDE1NC4zODVDMzEuNjQ0MyAxNTQuNTE4IDMxLjc3NTggMTU0LjYyMiAzMS45Mjk0IDE1NC42OTdDMzIuMDg1NyAxNTQuNzczIDMyLjI2MjcgMTU0LjgxMSAzMi40NjA3IDE1NC44MTFDMzIuNzE1OSAxNTQuODExIDMyLjkzMiAxNTQuNzU4IDMzLjEwOTEgMTU0LjY1NEMzMy4yODYyIDE1NC41NSAzMy40NDExIDE1NC40MTEgMzMuNTczOSAxNTQuMjM2TDM0LjAxMTQgMTU0LjU4NEMzMy45MjAzIDE1NC43MjIgMzMuODA0NCAxNTQuODU0IDMzLjY2MzggMTU0Ljk3OUMzMy41MjMyIDE1NS4xMDQgMzMuMzUgMTU1LjIwNSAzMy4xNDQzIDE1NS4yODNDMzIuOTQxMSAxNTUuMzYxIDMyLjcwMDIgMTU1LjQgMzIuNDIxNiAxNTUuNFpNMzQuOTg4NiAxNDkuMzIySDM1LjcxNTJWMTU0LjUwMkwzNS42NTI3IDE1NS4zMjJIMzQuOTg4NlYxNDkuMzIyWk0zOC41NzA2IDE1My4xNzRWMTUzLjI1NkMzOC41NzA2IDE1My41NjMgMzguNTM0MiAxNTMuODQ4IDM4LjQ2MTMgMTU0LjExMUMzOC4zODgzIDE1NC4zNzIgMzguMjgxNiAxNTQuNTk4IDM4LjE0MDkgMTU0Ljc5MUMzOC4wMDAzIDE1NC45ODQgMzcuODI4NCAxNTUuMTMzIDM3LjYyNTMgMTU1LjI0QzM3LjQyMjIgMTU1LjM0NyAzNy4xODkxIDE1NS40IDM2LjkyNjEgMTU1LjRDMzYuNjU3OSAxNTUuNCAzNi40MjIyIDE1NS4zNTUgMzYuMjE5MSAxNTUuMjY0QzM2LjAxODUgMTU1LjE3IDM1Ljg0OTMgMTU1LjAzNiAzNS43MTEzIDE1NC44NjFDMzUuNTczMiAxNTQuNjg3IDM1LjQ2MjYgMTU0LjQ3NiAzNS4zNzkyIDE1NC4yMjlDMzUuMjk4NSAxNTMuOTgxIDM1LjI0MjUgMTUzLjcwMiAzNS4yMTEzIDE1My4zOTNWMTUzLjAzM0MzNS4yNDI1IDE1Mi43MjEgMzUuMjk4NSAxNTIuNDQxIDM1LjM3OTIgMTUyLjE5M0MzNS40NjI2IDE1MS45NDYgMzUuNTczMiAxNTEuNzM1IDM1LjcxMTMgMTUxLjU2MUMzNS44NDkzIDE1MS4zODMgMzYuMDE4NSAxNTEuMjQ5IDM2LjIxOTEgMTUxLjE1OEMzNi40MTk2IDE1MS4wNjQgMzYuNjUyNyAxNTEuMDE4IDM2LjkxODMgMTUxLjAxOEMzNy4xODM5IDE1MS4wMTggMzcuNDE5NiAxNTEuMDcgMzcuNjI1MyAxNTEuMTc0QzM3LjgzMSAxNTEuMjc1IDM4LjAwMjkgMTUxLjQyMSAzOC4xNDA5IDE1MS42MTFDMzguMjgxNiAxNTEuODAxIDM4LjM4ODMgMTUyLjAyOSAzOC40NjEzIDE1Mi4yOTVDMzguNTM0MiAxNTIuNTU4IDM4LjU3MDYgMTUyLjg1MSAzOC41NzA2IDE1My4xNzRaTTM3Ljg0NDEgMTUzLjI1NlYxNTMuMTc0QzM3Ljg0NDEgMTUyLjk2MyAzNy44MjQ1IDE1Mi43NjUgMzcuNzg1NSAxNTIuNThDMzcuNzQ2NCAxNTIuMzkzIDM3LjY4MzkgMTUyLjIyOSAzNy41OTggMTUyLjA4OEMzNy41MTIgMTUxLjk0NSAzNy4zOTg4IDE1MS44MzMgMzcuMjU4MSAxNTEuNzUyQzM3LjExNzUgMTUxLjY2OSAzNi45NDQzIDE1MS42MjcgMzYuNzM4NiAxNTEuNjI3QzM2LjU1NjMgMTUxLjYyNyAzNi4zOTc1IDE1MS42NTggMzYuMjYyIDE1MS43MjFDMzYuMTI5MiAxNTEuNzgzIDM2LjAxNTkgMTUxLjg2OCAzNS45MjIyIDE1MS45NzVDMzUuODI4NCAxNTIuMDc5IDM1Ljc1MTYgMTUyLjE5OSAzNS42OTE3IDE1Mi4zMzRDMzUuNjM0NCAxNTIuNDY3IDM1LjU5MTUgMTUyLjYwNSAzNS41NjI4IDE1Mi43NDhWMTUzLjY4OUMzNS42MDQ1IDE1My44NzIgMzUuNjcyMiAxNTQuMDQ4IDM1Ljc2NTkgMTU0LjIxN0MzNS44NjIzIDE1NC4zODMgMzUuOTg5OSAxNTQuNTIgMzYuMTQ4OCAxNTQuNjI3QzM2LjMxMDIgMTU0LjczNCAzNi41MDk0IDE1NC43ODcgMzYuNzQ2NCAxNTQuNzg3QzM2Ljk0MTcgMTU0Ljc4NyAzNy4xMDg0IDE1NC43NDggMzcuMjQ2NCAxNTQuNjdDMzcuMzg3IDE1NC41ODkgMzcuNTAwMyAxNTQuNDc5IDM3LjU4NjMgMTU0LjMzOEMzNy42NzQ4IDE1NC4xOTcgMzcuNzM5OSAxNTQuMDM1IDM3Ljc4MTYgMTUzLjg1QzM3LjgyMzIgMTUzLjY2NSAzNy44NDQxIDE1My40NjcgMzcuODQ0MSAxNTMuMjU2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDFfNDQzN185Njg1OCkiPgo8bGluZSB4MT0iNjEuODUwMSIgeTE9IjE0Ny4wNzIiIHgyPSI2MS44NTAxIiB5Mj0iMTQ2LjI1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNNTMuOTA5IDE1Mi4wMjVWMTUyLjg5M0M1My45MDkgMTUzLjM1OSA1My44NjczIDE1My43NTIgNTMuNzg0IDE1NC4wNzJDNTMuNzAwNiAxNTQuMzkzIDUzLjU4MDggMTU0LjY1IDUzLjQyNDYgMTU0Ljg0NkM1My4yNjgzIDE1NS4wNDEgNTMuMDc5NSAxNTUuMTgzIDUyLjg1ODIgMTU1LjI3MUM1Mi42Mzk0IDE1NS4zNTcgNTIuMzkyIDE1NS40IDUyLjExNiAxNTUuNEM1MS44OTcyIDE1NS40IDUxLjY5NTQgMTU1LjM3MyA1MS41MTA1IDE1NS4zMThDNTEuMzI1NiAxNTUuMjY0IDUxLjE1OSAxNTUuMTc2IDUxLjAxMDUgMTU1LjA1N0M1MC44NjQ3IDE1NC45MzQgNTAuNzM5NyAxNTQuNzc1IDUwLjYzNTUgMTU0LjU4QzUwLjUzMTQgMTU0LjM4NSA1MC40NTE5IDE1NC4xNDggNTAuMzk3MiAxNTMuODY5QzUwLjM0MjYgMTUzLjU5IDUwLjMxNTIgMTUzLjI2NSA1MC4zMTUyIDE1Mi44OTNWMTUyLjAyNUM1MC4zMTUyIDE1MS41NTkgNTAuMzU2OSAxNTEuMTY5IDUwLjQ0MDIgMTUwLjg1NEM1MC41MjYxIDE1MC41MzggNTAuNjQ3MiAxNTAuMjg2IDUwLjgwMzUgMTUwLjA5NkM1MC45NTk3IDE0OS45MDMgNTEuMTQ3MiAxNDkuNzY1IDUxLjM2NiAxNDkuNjgyQzUxLjU4NzMgMTQ5LjU5OCA1MS44MzQ3IDE0OS41NTcgNTIuMTA4MiAxNDkuNTU3QzUyLjMyOTUgMTQ5LjU1NyA1Mi41MzI3IDE0OS41ODQgNTIuNzE3NiAxNDkuNjM5QzUyLjkwNTEgMTQ5LjY5MSA1My4wNzE3IDE0OS43NzUgNTMuMjE3NiAxNDkuODkzQzUzLjM2MzQgMTUwLjAwNyA1My40ODcxIDE1MC4xNjEgNTMuNTg4NiAxNTAuMzU0QzUzLjY5MjggMTUwLjU0NCA1My43NzIyIDE1MC43NzcgNTMuODI2OSAxNTEuMDUzQzUzLjg4MTYgMTUxLjMyOSA1My45MDkgMTUxLjY1MyA1My45MDkgMTUyLjAyNVpNNTMuMTgyNCAxNTMuMDFWMTUxLjkwNEM1My4xODI0IDE1MS42NDkgNTMuMTY2OCAxNTEuNDI1IDUzLjEzNTUgMTUxLjIzMkM1My4xMDY5IDE1MS4wMzcgNTMuMDYzOSAxNTAuODcgNTMuMDA2NiAxNTAuNzMyQzUyLjk0OTMgMTUwLjU5NCA1Mi44NzY0IDE1MC40ODIgNTIuNzg3OSAxNTAuMzk2QzUyLjcwMTkgMTUwLjMxMSA1Mi42MDE3IDE1MC4yNDggNTIuNDg3MSAxNTAuMjA5QzUyLjM3NTEgMTUwLjE2NyA1Mi4yNDg4IDE1MC4xNDYgNTIuMTA4MiAxNTAuMTQ2QzUxLjkzNjMgMTUwLjE0NiA1MS43ODQgMTUwLjE3OSA1MS42NTExIDE1MC4yNDRDNTEuNTE4MyAxNTAuMzA3IDUxLjQwNjQgMTUwLjQwNyA1MS4zMTUyIDE1MC41NDVDNTEuMjI2NyAxNTAuNjgzIDUxLjE1OSAxNTAuODY0IDUxLjExMjEgMTUxLjA4OEM1MS4wNjUyIDE1MS4zMTIgNTEuMDQxOCAxNTEuNTg0IDUxLjA0MTggMTUxLjkwNFYxNTMuMDFDNTEuMDQxOCAxNTMuMjY1IDUxLjA1NjEgMTUzLjQ5IDUxLjA4NDcgMTUzLjY4NkM1MS4xMTYgMTUzLjg4MSA1MS4xNjE2IDE1NC4wNSA1MS4yMjE1IDE1NC4xOTNDNTEuMjgxNCAxNTQuMzM0IDUxLjM1NDMgMTU0LjQ1IDUxLjQ0MDIgMTU0LjU0MUM1MS41MjYxIDE1NC42MzIgNTEuNjI1MSAxNTQuNyA1MS43MzcxIDE1NC43NDRDNTEuODUxNyAxNTQuNzg2IDUxLjk3OCAxNTQuODA3IDUyLjExNiAxNTQuODA3QzUyLjI5MzEgMTU0LjgwNyA1Mi40NDggMTU0Ljc3MyA1Mi41ODA4IDE1NC43MDVDNTIuNzEzNiAxNTQuNjM3IDUyLjgyNDMgMTU0LjUzMiA1Mi45MTI5IDE1NC4zODlDNTMuMDA0IDE1NC4yNDMgNTMuMDcxNyAxNTQuMDU3IDUzLjExNiAxNTMuODNDNTMuMTYwMyAxNTMuNjAxIDUzLjE4MjQgMTUzLjMyNyA1My4xODI0IDE1My4wMVpNNTguNjQ3OCAxNTQuNzI5VjE1NS4zMjJINTQuOTI1MlYxNTQuODAzTDU2Ljc4ODUgMTUyLjcyOUM1Ny4wMTc2IDE1Mi40NzMgNTcuMTk0NyAxNTIuMjU3IDU3LjMxOTcgMTUyLjA4QzU3LjQ0NzMgMTUxLjkgNTcuNTM1OSAxNTEuNzQgNTcuNTg1MyAxNTEuNkM1Ny42Mzc0IDE1MS40NTYgNTcuNjYzNSAxNTEuMzExIDU3LjY2MzUgMTUxLjE2MkM1Ny42NjM1IDE1MC45NzUgNTcuNjI0NCAxNTAuODA1IDU3LjU0NjMgMTUwLjY1NEM1Ny40NzA4IDE1MC41MDEgNTcuMzU4OCAxNTAuMzc4IDU3LjIxMDMgMTUwLjI4N0M1Ny4wNjE5IDE1MC4xOTYgNTYuODgyMiAxNTAuMTUgNTYuNjcxMyAxNTAuMTVDNTYuNDE4NyAxNTAuMTUgNTYuMjA3NyAxNTAuMiA1Ni4wMzg1IDE1MC4yOTlDNTUuODcxOCAxNTAuMzk1IDU1Ljc0NjggMTUwLjUzMSA1NS42NjM1IDE1MC43MDVDNTUuNTgwMSAxNTAuODggNTUuNTM4NSAxNTEuMDggNTUuNTM4NSAxNTEuMzA3SDU0LjgxNThDNTQuODE1OCAxNTAuOTg2IDU0Ljg4NjEgMTUwLjY5MyA1NS4wMjY3IDE1MC40MjhDNTUuMTY3NCAxNTAuMTYyIDU1LjM3NTcgMTQ5Ljk1MSA1NS42NTE3IDE0OS43OTVDNTUuOTI3OCAxNDkuNjM2IDU2LjI2NzYgMTQ5LjU1NyA1Ni42NzEzIDE0OS41NTdDNTcuMDMwNyAxNDkuNTU3IDU3LjMzNzkgMTQ5LjYyIDU3LjU5MzIgMTQ5Ljc0OEM1Ny44NDg0IDE0OS44NzMgNTguMDQzNyAxNTAuMDUgNTguMTc5MSAxNTAuMjc5QzU4LjMxNzEgMTUwLjUwNiA1OC4zODYxIDE1MC43NzEgNTguMzg2MSAxNTEuMDc2QzU4LjM4NjEgMTUxLjI0MyA1OC4zNTc1IDE1MS40MTIgNTguMzAwMiAxNTEuNTg0QzU4LjI0NTUgMTUxLjc1MyA1OC4xNjg3IDE1MS45MjMgNTguMDY5NyAxNTIuMDkyQzU3Ljk3MzQgMTUyLjI2MSA1Ny44NjAxIDE1Mi40MjggNTcuNzI5OSAxNTIuNTkyQzU3LjYwMjMgMTUyLjc1NiA1Ny40NjU1IDE1Mi45MTcgNTcuMzE5NyAxNTMuMDc2TDU1Ljc5NjMgMTU0LjcyOUg1OC42NDc4Wk02Mi41MTIzIDE0OS42MzVWMTU1LjMyMkg2MS43NTg0VjE0OS42MzVINjIuNTEyM1pNNjQuODk1MSAxNTIuMTkzVjE1Mi44MTFINjIuMzQ4MlYxNTIuMTkzSDY0Ljg5NTFaTTY1LjI4MTggMTQ5LjYzNVYxNTAuMjUySDYyLjM0ODJWMTQ5LjYzNUg2NS4yODE4Wk02Ny44MjE1IDE1NS40QzY3LjUyNzIgMTU1LjQgNjcuMjYwMyAxNTUuMzUxIDY3LjAyMDcgMTU1LjI1MkM2Ni43ODM3IDE1NS4xNSA2Ni41NzkzIDE1NS4wMDggNjYuNDA3NCAxNTQuODI2QzY2LjIzODIgMTU0LjY0NCA2Ni4xMDggMTU0LjQyOCA2Ni4wMTY4IDE1NC4xNzhDNjUuOTI1NyAxNTMuOTI4IDY1Ljg4MDEgMTUzLjY1NCA2NS44ODAxIDE1My4zNTdWMTUzLjE5M0M2NS44ODAxIDE1Mi44NSA2NS45MzA5IDE1Mi41NDQgNjYuMDMyNCAxNTIuMjc1QzY2LjEzNCAxNTIuMDA1IDY2LjI3MiAxNTEuNzc1IDY2LjQ0NjUgMTUxLjU4OEM2Ni42MjEgMTUxLjQgNjYuODE4OSAxNTEuMjU4IDY3LjA0MDIgMTUxLjE2MkM2Ny4yNjE2IDE1MS4wNjYgNjcuNDkwOCAxNTEuMDE4IDY3LjcyNzcgMTUxLjAxOEM2OC4wMjk4IDE1MS4wMTggNjguMjkwMiAxNTEuMDcgNjguNTA5IDE1MS4xNzRDNjguNzMwNCAxNTEuMjc4IDY4LjkxMTMgMTUxLjQyNCA2OS4wNTIgMTUxLjYxMUM2OS4xOTI2IDE1MS43OTYgNjkuMjk2OCAxNTIuMDE1IDY5LjM2NDUgMTUyLjI2OEM2OS40MzIyIDE1Mi41MTggNjkuNDY2IDE1Mi43OTEgNjkuNDY2IDE1My4wODhWMTUzLjQxMkg2Ni4zMDk4VjE1Mi44MjJINjguNzQzNFYxNTIuNzY4QzY4LjczMyAxNTIuNTggNjguNjkzOSAxNTIuMzk4IDY4LjYyNjIgMTUyLjIyMUM2OC41NjExIDE1Mi4wNDQgNjguNDU2OSAxNTEuODk4IDY4LjMxMzcgMTUxLjc4M0M2OC4xNzA1IDE1MS42NjkgNjcuOTc1MSAxNTEuNjExIDY3LjcyNzcgMTUxLjYxMUM2Ny41NjM3IDE1MS42MTEgNjcuNDEyNiAxNTEuNjQ2IDY3LjI3NDYgMTUxLjcxN0M2Ny4xMzY2IDE1MS43ODUgNjcuMDE4MSAxNTEuODg2IDY2LjkxOTIgMTUyLjAyMUM2Ni44MjAyIDE1Mi4xNTcgNjYuNzQzNCAxNTIuMzIyIDY2LjY4ODcgMTUyLjUxOEM2Ni42MzQgMTUyLjcxMyA2Ni42MDY3IDE1Mi45MzggNjYuNjA2NyAxNTMuMTkzVjE1My4zNTdDNjYuNjA2NyAxNTMuNTU4IDY2LjYzNCAxNTMuNzQ3IDY2LjY4ODcgMTUzLjkyNEM2Ni43NDYgMTU0LjA5OCA2Ni44MjggMTU0LjI1MiA2Ni45MzQ4IDE1NC4zODVDNjcuMDQ0MiAxNTQuNTE4IDY3LjE3NTcgMTU0LjYyMiA2Ny4zMjkzIDE1NC42OTdDNjcuNDg1NiAxNTQuNzczIDY3LjY2MjYgMTU0LjgxMSA2Ny44NjA2IDE1NC44MTFDNjguMTE1OCAxNTQuODExIDY4LjMzMTkgMTU0Ljc1OCA2OC41MDkgMTU0LjY1NEM2OC42ODYxIDE1NC41NSA2OC44NDEgMTU0LjQxMSA2OC45NzM4IDE1NC4yMzZMNjkuNDExMyAxNTQuNTg0QzY5LjMyMDIgMTU0LjcyMiA2OS4yMDQzIDE1NC44NTQgNjkuMDYzNyAxNTQuOTc5QzY4LjkyMzEgMTU1LjEwNCA2OC43NDk5IDE1NS4yMDUgNjguNTQ0MiAxNTUuMjgzQzY4LjM0MSAxNTUuMzYxIDY4LjEwMDEgMTU1LjQgNjcuODIxNSAxNTUuNFpNNzAuMzg4NSAxNDkuMzIySDcxLjExNTFWMTU0LjUwMkw3MS4wNTI2IDE1NS4zMjJINzAuMzg4NVYxNDkuMzIyWk03My45NzA1IDE1My4xNzRWMTUzLjI1NkM3My45NzA1IDE1My41NjMgNzMuOTM0MSAxNTMuODQ4IDczLjg2MTIgMTU0LjExMUM3My43ODgyIDE1NC4zNzIgNzMuNjgxNSAxNTQuNTk4IDczLjU0MDggMTU0Ljc5MUM3My40MDAyIDE1NC45ODQgNzMuMjI4MyAxNTUuMTMzIDczLjAyNTIgMTU1LjI0QzcyLjgyMjEgMTU1LjM0NyA3Mi41ODkgMTU1LjQgNzIuMzI2IDE1NS40QzcyLjA1NzggMTU1LjQgNzEuODIyMSAxNTUuMzU1IDcxLjYxOSAxNTUuMjY0QzcxLjQxODUgMTU1LjE3IDcxLjI0OTIgMTU1LjAzNiA3MS4xMTEyIDE1NC44NjFDNzAuOTczMSAxNTQuNjg3IDcwLjg2MjUgMTU0LjQ3NiA3MC43NzkxIDE1NC4yMjlDNzAuNjk4NCAxNTMuOTgxIDcwLjY0MjQgMTUzLjcwMiA3MC42MTEyIDE1My4zOTNWMTUzLjAzM0M3MC42NDI0IDE1Mi43MjEgNzAuNjk4NCAxNTIuNDQxIDcwLjc3OTEgMTUyLjE5M0M3MC44NjI1IDE1MS45NDYgNzAuOTczMSAxNTEuNzM1IDcxLjExMTIgMTUxLjU2MUM3MS4yNDkyIDE1MS4zODMgNzEuNDE4NSAxNTEuMjQ5IDcxLjYxOSAxNTEuMTU4QzcxLjgxOTUgMTUxLjA2NCA3Mi4wNTI2IDE1MS4wMTggNzIuMzE4MiAxNTEuMDE4QzcyLjU4MzggMTUxLjAxOCA3Mi44MTk1IDE1MS4wNyA3My4wMjUyIDE1MS4xNzRDNzMuMjMxIDE1MS4yNzUgNzMuNDAyOCAxNTEuNDIxIDczLjU0MDggMTUxLjYxMUM3My42ODE1IDE1MS44MDEgNzMuNzg4MiAxNTIuMDI5IDczLjg2MTIgMTUyLjI5NUM3My45MzQxIDE1Mi41NTggNzMuOTcwNSAxNTIuODUxIDczLjk3MDUgMTUzLjE3NFpNNzMuMjQ0IDE1My4yNTZWMTUzLjE3NEM3My4yNDQgMTUyLjk2MyA3My4yMjQ0IDE1Mi43NjUgNzMuMTg1NCAxNTIuNThDNzMuMTQ2MyAxNTIuMzkzIDczLjA4MzggMTUyLjIyOSA3Mi45OTc5IDE1Mi4wODhDNzIuOTExOSAxNTEuOTQ1IDcyLjc5ODcgMTUxLjgzMyA3Mi42NTggMTUxLjc1MkM3Mi41MTc0IDE1MS42NjkgNzIuMzQ0MiAxNTEuNjI3IDcyLjEzODUgMTUxLjYyN0M3MS45NTYyIDE1MS42MjcgNzEuNzk3NCAxNTEuNjU4IDcxLjY2MTkgMTUxLjcyMUM3MS41MjkxIDE1MS43ODMgNzEuNDE1OCAxNTEuODY4IDcxLjMyMjEgMTUxLjk3NUM3MS4yMjgzIDE1Mi4wNzkgNzEuMTUxNSAxNTIuMTk5IDcxLjA5MTYgMTUyLjMzNEM3MS4wMzQzIDE1Mi40NjcgNzAuOTkxNCAxNTIuNjA1IDcwLjk2MjcgMTUyLjc0OFYxNTMuNjg5QzcxLjAwNDQgMTUzLjg3MiA3MS4wNzIxIDE1NC4wNDggNzEuMTY1OCAxNTQuMjE3QzcxLjI2MjIgMTU0LjM4MyA3MS4zODk4IDE1NC41MiA3MS41NDg3IDE1NC42MjdDNzEuNzEwMSAxNTQuNzM0IDcxLjkwOTMgMTU0Ljc4NyA3Mi4xNDYzIDE1NC43ODdDNzIuMzQxNiAxNTQuNzg3IDcyLjUwODMgMTU0Ljc0OCA3Mi42NDYzIDE1NC42N0M3Mi43ODY5IDE1NC41ODkgNzIuOTAwMiAxNTQuNDc5IDcyLjk4NjIgMTU0LjMzOEM3My4wNzQ3IDE1NC4xOTcgNzMuMTM5OCAxNTQuMDM1IDczLjE4MTUgMTUzLjg1QzczLjIyMzEgMTUzLjY2NSA3My4yNDQgMTUzLjQ2NyA3My4yNDQgMTUzLjI1NloiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPC9nPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDJfNDQzN185Njg1OCkiPgo8bGluZSB4MT0iOTcuMjUiIHkxPSIxNDcuMDcyIiB4Mj0iOTcuMjUiIHkyPSIxNDYuMjUiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjUiIHN0cm9rZS13aWR0aD0iMC41IiBzdHJva2UtbGluZWNhcD0ic3F1YXJlIi8+CjxwYXRoIGQ9Ik04OS4zMDg5IDE1Mi4wMjVWMTUyLjg5M0M4OS4zMDg5IDE1My4zNTkgODkuMjY3MiAxNTMuNzUyIDg5LjE4MzkgMTU0LjA3MkM4OS4xMDA1IDE1NC4zOTMgODguOTgwNyAxNTQuNjUgODguODI0NSAxNTQuODQ2Qzg4LjY2ODIgMTU1LjA0MSA4OC40Nzk0IDE1NS4xODMgODguMjU4MSAxNTUuMjcxQzg4LjAzOTMgMTU1LjM1NyA4Ny43OTE5IDE1NS40IDg3LjUxNTkgMTU1LjRDODcuMjk3MSAxNTUuNCA4Ny4wOTUzIDE1NS4zNzMgODYuOTEwNCAxNTUuMzE4Qzg2LjcyNTUgMTU1LjI2NCA4Ni41NTg5IDE1NS4xNzYgODYuNDEwNCAxNTUuMDU3Qzg2LjI2NDYgMTU0LjkzNCA4Ni4xMzk2IDE1NC43NzUgODYuMDM1NCAxNTQuNThDODUuOTMxMyAxNTQuMzg1IDg1Ljg1MTggMTU0LjE0OCA4NS43OTcxIDE1My44NjlDODUuNzQyNSAxNTMuNTkgODUuNzE1MSAxNTMuMjY1IDg1LjcxNTEgMTUyLjg5M1YxNTIuMDI1Qzg1LjcxNTEgMTUxLjU1OSA4NS43NTY4IDE1MS4xNjkgODUuODQwMSAxNTAuODU0Qzg1LjkyNjEgMTUwLjUzOCA4Ni4wNDcxIDE1MC4yODYgODYuMjAzNCAxNTAuMDk2Qzg2LjM1OTYgMTQ5LjkwMyA4Ni41NDcxIDE0OS43NjUgODYuNzY1OSAxNDkuNjgyQzg2Ljk4NzIgMTQ5LjU5OCA4Ny4yMzQ2IDE0OS41NTcgODcuNTA4MSAxNDkuNTU3Qzg3LjcyOTQgMTQ5LjU1NyA4Ny45MzI2IDE0OS41ODQgODguMTE3NSAxNDkuNjM5Qzg4LjMwNSAxNDkuNjkxIDg4LjQ3MTYgMTQ5Ljc3NSA4OC42MTc1IDE0OS44OTNDODguNzYzMyAxNTAuMDA3IDg4Ljg4NyAxNTAuMTYxIDg4Ljk4ODYgMTUwLjM1NEM4OS4wOTI3IDE1MC41NDQgODkuMTcyMSAxNTAuNzc3IDg5LjIyNjggMTUxLjA1M0M4OS4yODE1IDE1MS4zMjkgODkuMzA4OSAxNTEuNjUzIDg5LjMwODkgMTUyLjAyNVpNODguNTgyMyAxNTMuMDFWMTUxLjkwNEM4OC41ODIzIDE1MS42NDkgODguNTY2NyAxNTEuNDI1IDg4LjUzNTQgMTUxLjIzMkM4OC41MDY4IDE1MS4wMzcgODguNDYzOCAxNTAuODcgODguNDA2NSAxNTAuNzMyQzg4LjM0OTIgMTUwLjU5NCA4OC4yNzYzIDE1MC40ODIgODguMTg3OCAxNTAuMzk2Qzg4LjEwMTggMTUwLjMxMSA4OC4wMDE2IDE1MC4yNDggODcuODg3IDE1MC4yMDlDODcuNzc1IDE1MC4xNjcgODcuNjQ4NyAxNTAuMTQ2IDg3LjUwODEgMTUwLjE0NkM4Ny4zMzYyIDE1MC4xNDYgODcuMTgzOSAxNTAuMTc5IDg3LjA1MTEgMTUwLjI0NEM4Ni45MTgyIDE1MC4zMDcgODYuODA2MyAxNTAuNDA3IDg2LjcxNTEgMTUwLjU0NUM4Ni42MjY2IDE1MC42ODMgODYuNTU4OSAxNTAuODY0IDg2LjUxMiAxNTEuMDg4Qzg2LjQ2NTEgMTUxLjMxMiA4Ni40NDE3IDE1MS41ODQgODYuNDQxNyAxNTEuOTA0VjE1My4wMUM4Ni40NDE3IDE1My4yNjUgODYuNDU2IDE1My40OSA4Ni40ODQ2IDE1My42ODZDODYuNTE1OSAxNTMuODgxIDg2LjU2MTUgMTU0LjA1IDg2LjYyMTQgMTU0LjE5M0M4Ni42ODEzIDE1NC4zMzQgODYuNzU0MiAxNTQuNDUgODYuODQwMSAxNTQuNTQxQzg2LjkyNjEgMTU0LjYzMiA4Ny4wMjUgMTU0LjcgODcuMTM3IDE1NC43NDRDODcuMjUxNiAxNTQuNzg2IDg3LjM3NzkgMTU0LjgwNyA4Ny41MTU5IDE1NC44MDdDODcuNjkzIDE1NC44MDcgODcuODQ3OSAxNTQuNzczIDg3Ljk4MDcgMTU0LjcwNUM4OC4xMTM2IDE1NC42MzcgODguMjI0MiAxNTQuNTMyIDg4LjMxMjggMTU0LjM4OUM4OC40MDM5IDE1NC4yNDMgODguNDcxNiAxNTQuMDU3IDg4LjUxNTkgMTUzLjgzQzg4LjU2MDIgMTUzLjYwMSA4OC41ODIzIDE1My4zMjcgODguNTgyMyAxNTMuMDFaTTkxLjM3NTkgMTUyLjEyM0g5MS44OTE1QzkyLjE0NDEgMTUyLjEyMyA5Mi4zNTI0IDE1Mi4wODEgOTIuNTE2NSAxNTEuOTk4QzkyLjY4MzIgMTUxLjkxMiA5Mi44MDY5IDE1MS43OTYgOTIuODg3NiAxNTEuNjVDOTIuOTcwOSAxNTEuNTAyIDkzLjAxMjYgMTUxLjMzNSA5My4wMTI2IDE1MS4xNUM5My4wMTI2IDE1MC45MzIgOTIuOTc2MSAxNTAuNzQ4IDkyLjkwMzIgMTUwLjZDOTIuODMwMyAxNTAuNDUxIDkyLjcyMDkgMTUwLjMzOSA5Mi41NzUxIDE1MC4yNjRDOTIuNDI5MyAxNTAuMTg4IDkyLjI0NDQgMTUwLjE1IDkyLjAyMDQgMTUwLjE1QzkxLjgxNzMgMTUwLjE1IDkxLjYzNzYgMTUwLjE5MSA5MS40ODEzIDE1MC4yNzFDOTEuMzI3NyAxNTAuMzUgOTEuMjA2NiAxNTAuNDYyIDkxLjExODEgMTUwLjYwN0M5MS4wMzIxIDE1MC43NTMgOTAuOTg5MSAxNTAuOTI1IDkwLjk4OTEgMTUxLjEyM0g5MC4yNjY1QzkwLjI2NjUgMTUwLjgzNCA5MC4zMzk0IDE1MC41NzEgOTAuNDg1MiAxNTAuMzM0QzkwLjYzMTEgMTUwLjA5NyA5MC44MzU1IDE0OS45MDggOTEuMDk4NSAxNDkuNzY4QzkxLjM2NDEgMTQ5LjYyNyA5MS42NzE0IDE0OS41NTcgOTIuMDIwNCAxNDkuNTU3QzkyLjM2NDEgMTQ5LjU1NyA5Mi42NjQ5IDE0OS42MTggOTIuOTIyNyAxNDkuNzRDOTMuMTgwNiAxNDkuODYgOTMuMzgxMSAxNTAuMDQgOTMuNTI0MyAxNTAuMjc5QzkzLjY2NzUgMTUwLjUxNiA5My43MzkxIDE1MC44MTIgOTMuNzM5MSAxNTEuMTY2QzkzLjczOTEgMTUxLjMwOSA5My43MDUzIDE1MS40NjMgOTMuNjM3NiAxNTEuNjI3QzkzLjU3MjUgMTUxLjc4OCA5My40Njk2IDE1MS45MzkgOTMuMzI5IDE1Mi4wOEM5My4xOTEgMTUyLjIyMSA5My4wMTEzIDE1Mi4zMzcgOTIuNzg5OSAxNTIuNDI4QzkyLjU2ODYgMTUyLjUxNiA5Mi4zMDI5IDE1Mi41NjEgOTEuOTkzMSAxNTIuNTYxSDkxLjM3NTlWMTUyLjEyM1pNOTEuMzc1OSAxNTIuNzE3VjE1Mi4yODNIOTEuOTkzMUM5Mi4zNTUgMTUyLjI4MyA5Mi42NTQ1IDE1Mi4zMjYgOTIuODkxNSAxNTIuNDEyQzkzLjEyODUgMTUyLjQ5OCA5My4zMTQ3IDE1Mi42MTMgOTMuNDUwMSAxNTIuNzU2QzkzLjU4ODEgMTUyLjg5OSA5My42ODQ1IDE1My4wNTcgOTMuNzM5MSAxNTMuMjI5QzkzLjc5NjQgMTUzLjM5OCA5My44MjUxIDE1My41NjcgOTMuODI1MSAxNTMuNzM2QzkzLjgyNTEgMTU0LjAwMiA5My43Nzk1IDE1NC4yMzggOTMuNjg4NCAxNTQuNDQzQzkzLjU5OTggMTU0LjY0OSA5My40NzM1IDE1NC44MjQgOTMuMzA5NSAxNTQuOTY3QzkzLjE0OCAxNTUuMTEgOTIuOTU3OSAxNTUuMjE4IDkyLjczOTEgMTU1LjI5MUM5Mi41MjA0IDE1NS4zNjQgOTIuMjgyMSAxNTUuNCA5Mi4wMjQzIDE1NS40QzkxLjc3NjkgMTU1LjQgOTEuNTQzOCAxNTUuMzY1IDkxLjMyNTEgMTU1LjI5NUM5MS4xMDg5IDE1NS4yMjUgOTAuOTE3NSAxNTUuMTIzIDkwLjc1MDkgMTU0Ljk5QzkwLjU4NDIgMTU0Ljg1NSA5MC40NTQgMTU0LjY4OSA5MC4zNjAyIDE1NC40OTRDOTAuMjY2NSAxNTQuMjk2IDkwLjIxOTYgMTU0LjA3MSA5MC4yMTk2IDE1My44MThIOTAuOTQyM0M5MC45NDIzIDE1NC4wMTYgOTAuOTg1MiAxNTQuMTg5IDkxLjA3MTIgMTU0LjMzOEM5MS4xNTk3IDE1NC40ODYgOTEuMjg0NyAxNTQuNjAyIDkxLjQ0NjIgMTU0LjY4NkM5MS42MTAyIDE1NC43NjYgOTEuODAyOSAxNTQuODA3IDkyLjAyNDMgMTU0LjgwN0M5Mi4yNDU3IDE1NC44MDcgOTIuNDM1OCAxNTQuNzY5IDkyLjU5NDYgMTU0LjY5M0M5Mi43NTYxIDE1NC42MTUgOTIuODc5OCAxNTQuNDk4IDkyLjk2NTcgMTU0LjM0MkM5My4wNTQzIDE1NC4xODYgOTMuMDk4NSAxNTMuOTg5IDkzLjA5ODUgMTUzLjc1MkM5My4wOTg1IDE1My41MTUgOTMuMDQ5IDE1My4zMjEgOTIuOTUwMSAxNTMuMTdDOTIuODUxMSAxNTMuMDE2IDkyLjcxMDUgMTUyLjkwMyA5Mi41MjgyIDE1Mi44M0M5Mi4zNDg1IDE1Mi43NTUgOTIuMTM2MyAxNTIuNzE3IDkxLjg5MTUgMTUyLjcxN0g5MS4zNzU5Wk05Ny45MTIyIDE0OS42MzVWMTU1LjMyMkg5Ny4xNTgzVjE0OS42MzVIOTcuOTEyMlpNMTAwLjI5NSAxNTIuMTkzVjE1Mi44MTFIOTcuNzQ4MVYxNTIuMTkzSDEwMC4yOTVaTTEwMC42ODIgMTQ5LjYzNVYxNTAuMjUySDk3Ljc0ODFWMTQ5LjYzNUgxMDAuNjgyWk0xMDMuMjIxIDE1NS40QzEwMi45MjcgMTU1LjQgMTAyLjY2IDE1NS4zNTEgMTAyLjQyMSAxNTUuMjUyQzEwMi4xODQgMTU1LjE1IDEwMS45NzkgMTU1LjAwOCAxMDEuODA3IDE1NC44MjZDMTAxLjYzOCAxNTQuNjQ0IDEwMS41MDggMTU0LjQyOCAxMDEuNDE3IDE1NC4xNzhDMTAxLjMyNiAxNTMuOTI4IDEwMS4yOCAxNTMuNjU0IDEwMS4yOCAxNTMuMzU3VjE1My4xOTNDMTAxLjI4IDE1Mi44NSAxMDEuMzMxIDE1Mi41NDQgMTAxLjQzMiAxNTIuMjc1QzEwMS41MzQgMTUyLjAwNSAxMDEuNjcyIDE1MS43NzUgMTAxLjg0NiAxNTEuNTg4QzEwMi4wMjEgMTUxLjQgMTAyLjIxOSAxNTEuMjU4IDEwMi40NCAxNTEuMTYyQzEwMi42NjIgMTUxLjA2NiAxMDIuODkxIDE1MS4wMTggMTAzLjEyOCAxNTEuMDE4QzEwMy40MyAxNTEuMDE4IDEwMy42OSAxNTEuMDcgMTAzLjkwOSAxNTEuMTc0QzEwNC4xMyAxNTEuMjc4IDEwNC4zMTEgMTUxLjQyNCAxMDQuNDUyIDE1MS42MTFDMTA0LjU5MiAxNTEuNzk2IDEwNC42OTcgMTUyLjAxNSAxMDQuNzY0IDE1Mi4yNjhDMTA0LjgzMiAxNTIuNTE4IDEwNC44NjYgMTUyLjc5MSAxMDQuODY2IDE1My4wODhWMTUzLjQxMkgxMDEuNzFWMTUyLjgyMkgxMDQuMTQzVjE1Mi43NjhDMTA0LjEzMyAxNTIuNTggMTA0LjA5NCAxNTIuMzk4IDEwNC4wMjYgMTUyLjIyMUMxMDMuOTYxIDE1Mi4wNDQgMTAzLjg1NyAxNTEuODk4IDEwMy43MTQgMTUxLjc4M0MxMDMuNTcgMTUxLjY2OSAxMDMuMzc1IDE1MS42MTEgMTAzLjEyOCAxNTEuNjExQzEwMi45NjQgMTUxLjYxMSAxMDIuODEzIDE1MS42NDYgMTAyLjY3NSAxNTEuNzE3QzEwMi41MzcgMTUxLjc4NSAxMDIuNDE4IDE1MS44ODYgMTAyLjMxOSAxNTIuMDIxQzEwMi4yMiAxNTIuMTU3IDEwMi4xNDMgMTUyLjMyMiAxMDIuMDg5IDE1Mi41MThDMTAyLjAzNCAxNTIuNzEzIDEwMi4wMDcgMTUyLjkzOCAxMDIuMDA3IDE1My4xOTNWMTUzLjM1N0MxMDIuMDA3IDE1My41NTggMTAyLjAzNCAxNTMuNzQ3IDEwMi4wODkgMTUzLjkyNEMxMDIuMTQ2IDE1NC4wOTggMTAyLjIyOCAxNTQuMjUyIDEwMi4zMzUgMTU0LjM4NUMxMDIuNDQ0IDE1NC41MTggMTAyLjU3NiAxNTQuNjIyIDEwMi43MjkgMTU0LjY5N0MxMDIuODg1IDE1NC43NzMgMTAzLjA2MyAxNTQuODExIDEwMy4yNiAxNTQuODExQzEwMy41MTYgMTU0LjgxMSAxMDMuNzMyIDE1NC43NTggMTAzLjkwOSAxNTQuNjU0QzEwNC4wODYgMTU0LjU1IDEwNC4yNDEgMTU0LjQxMSAxMDQuMzc0IDE1NC4yMzZMMTA0LjgxMSAxNTQuNTg0QzEwNC43MiAxNTQuNzIyIDEwNC42MDQgMTU0Ljg1NCAxMDQuNDY0IDE1NC45NzlDMTA0LjMyMyAxNTUuMTA0IDEwNC4xNSAxNTUuMjA1IDEwMy45NDQgMTU1LjI4M0MxMDMuNzQxIDE1NS4zNjEgMTAzLjUgMTU1LjQgMTAzLjIyMSAxNTUuNFpNMTA1Ljc4OCAxNDkuMzIySDEwNi41MTVWMTU0LjUwMkwxMDYuNDUyIDE1NS4zMjJIMTA1Ljc4OFYxNDkuMzIyWk0xMDkuMzcgMTUzLjE3NFYxNTMuMjU2QzEwOS4zNyAxNTMuNTYzIDEwOS4zMzQgMTUzLjg0OCAxMDkuMjYxIDE1NC4xMTFDMTA5LjE4OCAxNTQuMzcyIDEwOS4wODEgMTU0LjU5OCAxMDguOTQxIDE1NC43OTFDMTA4LjggMTU0Ljk4NCAxMDguNjI4IDE1NS4xMzMgMTA4LjQyNSAxNTUuMjRDMTA4LjIyMiAxNTUuMzQ3IDEwNy45ODkgMTU1LjQgMTA3LjcyNiAxNTUuNEMxMDcuNDU4IDE1NS40IDEwNy4yMjIgMTU1LjM1NSAxMDcuMDE5IDE1NS4yNjRDMTA2LjgxOCAxNTUuMTcgMTA2LjY0OSAxNTUuMDM2IDEwNi41MTEgMTU0Ljg2MUMxMDYuMzczIDE1NC42ODcgMTA2LjI2MiAxNTQuNDc2IDEwNi4xNzkgMTU0LjIyOUMxMDYuMDk4IDE1My45ODEgMTA2LjA0MiAxNTMuNzAyIDEwNi4wMTEgMTUzLjM5M1YxNTMuMDMzQzEwNi4wNDIgMTUyLjcyMSAxMDYuMDk4IDE1Mi40NDEgMTA2LjE3OSAxNTIuMTkzQzEwNi4yNjIgMTUxLjk0NiAxMDYuMzczIDE1MS43MzUgMTA2LjUxMSAxNTEuNTYxQzEwNi42NDkgMTUxLjM4MyAxMDYuODE4IDE1MS4yNDkgMTA3LjAxOSAxNTEuMTU4QzEwNy4yMTkgMTUxLjA2NCAxMDcuNDUyIDE1MS4wMTggMTA3LjcxOCAxNTEuMDE4QzEwNy45ODQgMTUxLjAxOCAxMDguMjE5IDE1MS4wNyAxMDguNDI1IDE1MS4xNzRDMTA4LjYzMSAxNTEuMjc1IDEwOC44MDMgMTUxLjQyMSAxMDguOTQxIDE1MS42MTFDMTA5LjA4MSAxNTEuODAxIDEwOS4xODggMTUyLjAyOSAxMDkuMjYxIDE1Mi4yOTVDMTA5LjMzNCAxNTIuNTU4IDEwOS4zNyAxNTIuODUxIDEwOS4zNyAxNTMuMTc0Wk0xMDguNjQ0IDE1My4yNTZWMTUzLjE3NEMxMDguNjQ0IDE1Mi45NjMgMTA4LjYyNCAxNTIuNzY1IDEwOC41ODUgMTUyLjU4QzEwOC41NDYgMTUyLjM5MyAxMDguNDg0IDE1Mi4yMjkgMTA4LjM5OCAxNTIuMDg4QzEwOC4zMTIgMTUxLjk0NSAxMDguMTk5IDE1MS44MzMgMTA4LjA1OCAxNTEuNzUyQzEwNy45MTcgMTUxLjY2OSAxMDcuNzQ0IDE1MS42MjcgMTA3LjUzOCAxNTEuNjI3QzEwNy4zNTYgMTUxLjYyNyAxMDcuMTk3IDE1MS42NTggMTA3LjA2MiAxNTEuNzIxQzEwNi45MjkgMTUxLjc4MyAxMDYuODE2IDE1MS44NjggMTA2LjcyMiAxNTEuOTc1QzEwNi42MjggMTUyLjA3OSAxMDYuNTUxIDE1Mi4xOTkgMTA2LjQ5MiAxNTIuMzM0QzEwNi40MzQgMTUyLjQ2NyAxMDYuMzkxIDE1Mi42MDUgMTA2LjM2MyAxNTIuNzQ4VjE1My42ODlDMTA2LjQwNCAxNTMuODcyIDEwNi40NzIgMTU0LjA0OCAxMDYuNTY2IDE1NC4yMTdDMTA2LjY2MiAxNTQuMzgzIDEwNi43OSAxNTQuNTIgMTA2Ljk0OSAxNTQuNjI3QzEwNy4xMSAxNTQuNzM0IDEwNy4zMDkgMTU0Ljc4NyAxMDcuNTQ2IDE1NC43ODdDMTA3Ljc0MiAxNTQuNzg3IDEwNy45MDggMTU0Ljc0OCAxMDguMDQ2IDE1NC42N0MxMDguMTg3IDE1NC41ODkgMTA4LjMgMTU0LjQ3OSAxMDguMzg2IDE1NC4zMzhDMTA4LjQ3NSAxNTQuMTk3IDEwOC41NCAxNTQuMDM1IDEwOC41ODEgMTUzLjg1QzEwOC42MjMgMTUzLjY2NSAxMDguNjQ0IDE1My40NjcgMTA4LjY0NCAxNTMuMjU2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8L2c+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwM180NDM3Xzk2ODU4KSI+CjxsaW5lIHgxPSIxMzIuNjUiIHkxPSIxNDcuMDcyIiB4Mj0iMTMyLjY1IiB5Mj0iMTQ2LjI1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNMTI0LjcwOSAxNTIuMDI1VjE1Mi44OTNDMTI0LjcwOSAxNTMuMzU5IDEyNC42NjggMTUzLjc1MiAxMjQuNTg0IDE1NC4wNzJDMTI0LjUwMSAxNTQuMzkzIDEyNC4zODEgMTU0LjY1IDEyNC4yMjUgMTU0Ljg0NkMxMjQuMDY5IDE1NS4wNDEgMTIzLjg4IDE1NS4xODMgMTIzLjY1OCAxNTUuMjcxQzEyMy40NCAxNTUuMzU3IDEyMy4xOTIgMTU1LjQgMTIyLjkxNiAxNTUuNEMxMjIuNjk4IDE1NS40IDEyMi40OTYgMTU1LjM3MyAxMjIuMzExIDE1NS4zMThDMTIyLjEyNiAxNTUuMjY0IDEyMS45NTkgMTU1LjE3NiAxMjEuODExIDE1NS4wNTdDMTIxLjY2NSAxNTQuOTM0IDEyMS41NCAxNTQuNzc1IDEyMS40MzYgMTU0LjU4QzEyMS4zMzIgMTU0LjM4NSAxMjEuMjUyIDE1NC4xNDggMTIxLjE5OCAxNTMuODY5QzEyMS4xNDMgMTUzLjU5IDEyMS4xMTYgMTUzLjI2NSAxMjEuMTE2IDE1Mi44OTNWMTUyLjAyNUMxMjEuMTE2IDE1MS41NTkgMTIxLjE1NyAxNTEuMTY5IDEyMS4yNDEgMTUwLjg1NEMxMjEuMzI2IDE1MC41MzggMTIxLjQ0OCAxNTAuMjg2IDEyMS42MDQgMTUwLjA5NkMxMjEuNzYgMTQ5LjkwMyAxMjEuOTQ4IDE0OS43NjUgMTIyLjE2NiAxNDkuNjgyQzEyMi4zODggMTQ5LjU5OCAxMjIuNjM1IDE0OS41NTcgMTIyLjkwOCAxNDkuNTU3QzEyMy4xMyAxNDkuNTU3IDEyMy4zMzMgMTQ5LjU4NCAxMjMuNTE4IDE0OS42MzlDMTIzLjcwNSAxNDkuNjkxIDEyMy44NzIgMTQ5Ljc3NSAxMjQuMDE4IDE0OS44OTNDMTI0LjE2NCAxNTAuMDA3IDEyNC4yODcgMTUwLjE2MSAxMjQuMzg5IDE1MC4zNTRDMTI0LjQ5MyAxNTAuNTQ0IDEyNC41NzMgMTUwLjc3NyAxMjQuNjI3IDE1MS4wNTNDMTI0LjY4MiAxNTEuMzI5IDEyNC43MDkgMTUxLjY1MyAxMjQuNzA5IDE1Mi4wMjVaTTEyMy45ODMgMTUzLjAxVjE1MS45MDRDMTIzLjk4MyAxNTEuNjQ5IDEyMy45NjcgMTUxLjQyNSAxMjMuOTM2IDE1MS4yMzJDMTIzLjkwNyAxNTEuMDM3IDEyMy44NjQgMTUwLjg3IDEyMy44MDcgMTUwLjczMkMxMjMuNzUgMTUwLjU5NCAxMjMuNjc3IDE1MC40ODIgMTIzLjU4OCAxNTAuMzk2QzEyMy41MDIgMTUwLjMxMSAxMjMuNDAyIDE1MC4yNDggMTIzLjI4NyAxNTAuMjA5QzEyMy4xNzUgMTUwLjE2NyAxMjMuMDQ5IDE1MC4xNDYgMTIyLjkwOCAxNTAuMTQ2QzEyMi43MzcgMTUwLjE0NiAxMjIuNTg0IDE1MC4xNzkgMTIyLjQ1MSAxNTAuMjQ0QzEyMi4zMTkgMTUwLjMwNyAxMjIuMjA3IDE1MC40MDcgMTIyLjExNiAxNTAuNTQ1QzEyMi4wMjcgMTUwLjY4MyAxMjEuOTU5IDE1MC44NjQgMTIxLjkxMiAxNTEuMDg4QzEyMS44NjYgMTUxLjMxMiAxMjEuODQyIDE1MS41ODQgMTIxLjg0MiAxNTEuOTA0VjE1My4wMUMxMjEuODQyIDE1My4yNjUgMTIxLjg1NiAxNTMuNDkgMTIxLjg4NSAxNTMuNjg2QzEyMS45MTYgMTUzLjg4MSAxMjEuOTYyIDE1NC4wNSAxMjIuMDIyIDE1NC4xOTNDMTIyLjA4MiAxNTQuMzM0IDEyMi4xNTUgMTU0LjQ1IDEyMi4yNDEgMTU0LjU0MUMxMjIuMzI2IDE1NC42MzIgMTIyLjQyNSAxNTQuNyAxMjIuNTM3IDE1NC43NDRDMTIyLjY1MiAxNTQuNzg2IDEyMi43NzggMTU0LjgwNyAxMjIuOTE2IDE1NC44MDdDMTIzLjA5MyAxNTQuODA3IDEyMy4yNDggMTU0Ljc3MyAxMjMuMzgxIDE1NC43MDVDMTIzLjUxNCAxNTQuNjM3IDEyMy42MjUgMTU0LjUzMiAxMjMuNzEzIDE1NC4zODlDMTIzLjgwNCAxNTQuMjQzIDEyMy44NzIgMTU0LjA1NyAxMjMuOTE2IDE1My44M0MxMjMuOTYxIDE1My42MDEgMTIzLjk4MyAxNTMuMzI3IDEyMy45ODMgMTUzLjAxWk0xMjkuNTY1IDE1My40MDhWMTU0LjAwMkgxMjUuNDU2VjE1My41NzZMMTI4LjAwMyAxNDkuNjM1SDEyOC41OTNMMTI3Ljk2IDE1MC43NzVMMTI2LjI3NiAxNTMuNDA4SDEyOS41NjVaTTEyOC43NzIgMTQ5LjYzNVYxNTUuMzIySDEyOC4wNVYxNDkuNjM1SDEyOC43NzJaTTEzMy4zMTMgMTQ5LjYzNVYxNTUuMzIySDEzMi41NTlWMTQ5LjYzNUgxMzMuMzEzWk0xMzUuNjk1IDE1Mi4xOTNWMTUyLjgxMUgxMzMuMTQ5VjE1Mi4xOTNIMTM1LjY5NVpNMTM2LjA4MiAxNDkuNjM1VjE1MC4yNTJIMTMzLjE0OVYxNDkuNjM1SDEzNi4wODJaTTEzOC42MjIgMTU1LjRDMTM4LjMyOCAxNTUuNCAxMzguMDYxIDE1NS4zNTEgMTM3LjgyMSAxNTUuMjUyQzEzNy41ODQgMTU1LjE1IDEzNy4zOCAxNTUuMDA4IDEzNy4yMDggMTU0LjgyNkMxMzcuMDM4IDE1NC42NDQgMTM2LjkwOCAxNTQuNDI4IDEzNi44MTcgMTU0LjE3OEMxMzYuNzI2IDE1My45MjggMTM2LjY4IDE1My42NTQgMTM2LjY4IDE1My4zNTdWMTUzLjE5M0MxMzYuNjggMTUyLjg1IDEzNi43MzEgMTUyLjU0NCAxMzYuODMzIDE1Mi4yNzVDMTM2LjkzNCAxNTIuMDA1IDEzNy4wNzIgMTUxLjc3NSAxMzcuMjQ3IDE1MS41ODhDMTM3LjQyMSAxNTEuNCAxMzcuNjE5IDE1MS4yNTggMTM3Ljg0MSAxNTEuMTYyQzEzOC4wNjIgMTUxLjA2NiAxMzguMjkxIDE1MS4wMTggMTM4LjUyOCAxNTEuMDE4QzEzOC44MyAxNTEuMDE4IDEzOS4wOTEgMTUxLjA3IDEzOS4zMDkgMTUxLjE3NEMxMzkuNTMxIDE1MS4yNzggMTM5LjcxMiAxNTEuNDI0IDEzOS44NTIgMTUxLjYxMUMxMzkuOTkzIDE1MS43OTYgMTQwLjA5NyAxNTIuMDE1IDE0MC4xNjUgMTUyLjI2OEMxNDAuMjMyIDE1Mi41MTggMTQwLjI2NiAxNTIuNzkxIDE0MC4yNjYgMTUzLjA4OFYxNTMuNDEySDEzNy4xMVYxNTIuODIySDEzOS41NDRWMTUyLjc2OEMxMzkuNTMzIDE1Mi41OCAxMzkuNDk0IDE1Mi4zOTggMTM5LjQyNiAxNTIuMjIxQzEzOS4zNjEgMTUyLjA0NCAxMzkuMjU3IDE1MS44OTggMTM5LjExNCAxNTEuNzgzQzEzOC45NzEgMTUxLjY2OSAxMzguNzc1IDE1MS42MTEgMTM4LjUyOCAxNTEuNjExQzEzOC4zNjQgMTUxLjYxMSAxMzguMjEzIDE1MS42NDYgMTM4LjA3NSAxNTEuNzE3QzEzNy45MzcgMTUxLjc4NSAxMzcuODE4IDE1MS44ODYgMTM3LjcxOSAxNTIuMDIxQzEzNy42MiAxNTIuMTU3IDEzNy41NDQgMTUyLjMyMiAxMzcuNDg5IDE1Mi41MThDMTM3LjQzNCAxNTIuNzEzIDEzNy40MDcgMTUyLjkzOCAxMzcuNDA3IDE1My4xOTNWMTUzLjM1N0MxMzcuNDA3IDE1My41NTggMTM3LjQzNCAxNTMuNzQ3IDEzNy40ODkgMTUzLjkyNEMxMzcuNTQ2IDE1NC4wOTggMTM3LjYyOCAxNTQuMjUyIDEzNy43MzUgMTU0LjM4NUMxMzcuODQ0IDE1NC41MTggMTM3Ljk3NiAxNTQuNjIyIDEzOC4xMyAxNTQuNjk3QzEzOC4yODYgMTU0Ljc3MyAxMzguNDYzIDE1NC44MTEgMTM4LjY2MSAxNTQuODExQzEzOC45MTYgMTU0LjgxMSAxMzkuMTMyIDE1NC43NTggMTM5LjMwOSAxNTQuNjU0QzEzOS40ODYgMTU0LjU1IDEzOS42NDEgMTU0LjQxMSAxMzkuNzc0IDE1NC4yMzZMMTQwLjIxMiAxNTQuNTg0QzE0MC4xMiAxNTQuNzIyIDE0MC4wMDUgMTU0Ljg1NCAxMzkuODY0IDE1NC45NzlDMTM5LjcyMyAxNTUuMTA0IDEzOS41NSAxNTUuMjA1IDEzOS4zNDQgMTU1LjI4M0MxMzkuMTQxIDE1NS4zNjEgMTM4LjkgMTU1LjQgMTM4LjYyMiAxNTUuNFpNMTQxLjE4OSAxNDkuMzIySDE0MS45MTVWMTU0LjUwMkwxNDEuODUzIDE1NS4zMjJIMTQxLjE4OVYxNDkuMzIyWk0xNDQuNzcxIDE1My4xNzRWMTUzLjI1NkMxNDQuNzcxIDE1My41NjMgMTQ0LjczNCAxNTMuODQ4IDE0NC42NjEgMTU0LjExMUMxNDQuNTg5IDE1NC4zNzIgMTQ0LjQ4MiAxNTQuNTk4IDE0NC4zNDEgMTU0Ljc5MUMxNDQuMjAxIDE1NC45ODQgMTQ0LjAyOSAxNTUuMTMzIDE0My44MjYgMTU1LjI0QzE0My42MjIgMTU1LjM0NyAxNDMuMzg5IDE1NS40IDE0My4xMjYgMTU1LjRDMTQyLjg1OCAxNTUuNCAxNDIuNjIyIDE1NS4zNTUgMTQyLjQxOSAxNTUuMjY0QzE0Mi4yMTkgMTU1LjE3IDE0Mi4wNDkgMTU1LjAzNiAxNDEuOTExIDE1NC44NjFDMTQxLjc3MyAxNTQuNjg3IDE0MS42NjMgMTU0LjQ3NiAxNDEuNTc5IDE1NC4yMjlDMTQxLjQ5OSAxNTMuOTgxIDE0MS40NDMgMTUzLjcwMiAxNDEuNDExIDE1My4zOTNWMTUzLjAzM0MxNDEuNDQzIDE1Mi43MjEgMTQxLjQ5OSAxNTIuNDQxIDE0MS41NzkgMTUyLjE5M0MxNDEuNjYzIDE1MS45NDYgMTQxLjc3MyAxNTEuNzM1IDE0MS45MTEgMTUxLjU2MUMxNDIuMDQ5IDE1MS4zODMgMTQyLjIxOSAxNTEuMjQ5IDE0Mi40MTkgMTUxLjE1OEMxNDIuNjIgMTUxLjA2NCAxNDIuODUzIDE1MS4wMTggMTQzLjExOCAxNTEuMDE4QzE0My4zODQgMTUxLjAxOCAxNDMuNjIgMTUxLjA3IDE0My44MjYgMTUxLjE3NEMxNDQuMDMxIDE1MS4yNzUgMTQ0LjIwMyAxNTEuNDIxIDE0NC4zNDEgMTUxLjYxMUMxNDQuNDgyIDE1MS44MDEgMTQ0LjU4OSAxNTIuMDI5IDE0NC42NjEgMTUyLjI5NUMxNDQuNzM0IDE1Mi41NTggMTQ0Ljc3MSAxNTIuODUxIDE0NC43NzEgMTUzLjE3NFpNMTQ0LjA0NCAxNTMuMjU2VjE1My4xNzRDMTQ0LjA0NCAxNTIuOTYzIDE0NC4wMjUgMTUyLjc2NSAxNDMuOTg2IDE1Mi41OEMxNDMuOTQ3IDE1Mi4zOTMgMTQzLjg4NCAxNTIuMjI5IDE0My43OTggMTUyLjA4OEMxNDMuNzEyIDE1MS45NDUgMTQzLjU5OSAxNTEuODMzIDE0My40NTggMTUxLjc1MkMxNDMuMzE4IDE1MS42NjkgMTQzLjE0NSAxNTEuNjI3IDE0Mi45MzkgMTUxLjYyN0MxNDIuNzU3IDE1MS42MjcgMTQyLjU5OCAxNTEuNjU4IDE0Mi40NjIgMTUxLjcyMUMxNDIuMzI5IDE1MS43ODMgMTQyLjIxNiAxNTEuODY4IDE0Mi4xMjIgMTUxLjk3NUMxNDIuMDI5IDE1Mi4wNzkgMTQxLjk1MiAxNTIuMTk5IDE0MS44OTIgMTUyLjMzNEMxNDEuODM1IDE1Mi40NjcgMTQxLjc5MiAxNTIuNjA1IDE0MS43NjMgMTUyLjc0OFYxNTMuNjg5QzE0MS44MDUgMTUzLjg3MiAxNDEuODcyIDE1NC4wNDggMTQxLjk2NiAxNTQuMjE3QzE0Mi4wNjIgMTU0LjM4MyAxNDIuMTkgMTU0LjUyIDE0Mi4zNDkgMTU0LjYyN0MxNDIuNTEgMTU0LjczNCAxNDIuNzEgMTU0Ljc4NyAxNDIuOTQ3IDE1NC43ODdDMTQzLjE0MiAxNTQuNzg3IDE0My4zMDkgMTU0Ljc0OCAxNDMuNDQ3IDE1NC42N0MxNDMuNTg3IDE1NC41ODkgMTQzLjcwMSAxNTQuNDc5IDE0My43ODYgMTU0LjMzOEMxNDMuODc1IDE1NC4xOTcgMTQzLjk0IDE1NC4wMzUgMTQzLjk4MiAxNTMuODVDMTQ0LjAyMyAxNTMuNjY1IDE0NC4wNDQgMTUzLjQ2NyAxNDQuMDQ0IDE1My4yNTZaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjwvZz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXA0XzQ0MzdfOTY4NTgpIj4KPGxpbmUgeDE9IjE2OC4wNSIgeTE9IjE0Ny4wNzIiIHgyPSIxNjguMDUiIHkyPSIxNDYuMjUiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjUiIHN0cm9rZS13aWR0aD0iMC41IiBzdHJva2UtbGluZWNhcD0ic3F1YXJlIi8+CjxwYXRoIGQ9Ik0xNjAuMTA5IDE1Mi4wMjVWMTUyLjg5M0MxNjAuMTA5IDE1My4zNTkgMTYwLjA2NyAxNTMuNzUyIDE1OS45ODQgMTU0LjA3MkMxNTkuOTAxIDE1NC4zOTMgMTU5Ljc4MSAxNTQuNjUgMTU5LjYyNSAxNTQuODQ2QzE1OS40NjkgMTU1LjA0MSAxNTkuMjggMTU1LjE4MyAxNTkuMDU4IDE1NS4yNzFDMTU4Ljg0IDE1NS4zNTcgMTU4LjU5MiAxNTUuNCAxNTguMzE2IDE1NS40QzE1OC4wOTcgMTU1LjQgMTU3Ljg5NiAxNTUuMzczIDE1Ny43MTEgMTU1LjMxOEMxNTcuNTI2IDE1NS4yNjQgMTU3LjM1OSAxNTUuMTc2IDE1Ny4yMTEgMTU1LjA1N0MxNTcuMDY1IDE1NC45MzQgMTU2Ljk0IDE1NC43NzUgMTU2LjgzNiAxNTQuNThDMTU2LjczMiAxNTQuMzg1IDE1Ni42NTIgMTU0LjE0OCAxNTYuNTk3IDE1My44NjlDMTU2LjU0MyAxNTMuNTkgMTU2LjUxNSAxNTMuMjY1IDE1Ni41MTUgMTUyLjg5M1YxNTIuMDI1QzE1Ni41MTUgMTUxLjU1OSAxNTYuNTU3IDE1MS4xNjkgMTU2LjY0IDE1MC44NTRDMTU2LjcyNiAxNTAuNTM4IDE1Ni44NDcgMTUwLjI4NiAxNTcuMDA0IDE1MC4wOTZDMTU3LjE2IDE0OS45MDMgMTU3LjM0NyAxNDkuNzY1IDE1Ny41NjYgMTQ5LjY4MkMxNTcuNzg4IDE0OS41OTggMTU4LjAzNSAxNDkuNTU3IDE1OC4zMDggMTQ5LjU1N0MxNTguNTMgMTQ5LjU1NyAxNTguNzMzIDE0OS41ODQgMTU4LjkxOCAxNDkuNjM5QzE1OS4xMDUgMTQ5LjY5MSAxNTkuMjcyIDE0OS43NzUgMTU5LjQxOCAxNDkuODkzQzE1OS41NjQgMTUwLjAwNyAxNTkuNjg3IDE1MC4xNjEgMTU5Ljc4OSAxNTAuMzU0QzE1OS44OTMgMTUwLjU0NCAxNTkuOTcyIDE1MC43NzcgMTYwLjAyNyAxNTEuMDUzQzE2MC4wODIgMTUxLjMyOSAxNjAuMTA5IDE1MS42NTMgMTYwLjEwOSAxNTIuMDI1Wk0xNTkuMzgzIDE1My4wMVYxNTEuOTA0QzE1OS4zODMgMTUxLjY0OSAxNTkuMzY3IDE1MS40MjUgMTU5LjMzNiAxNTEuMjMyQzE1OS4zMDcgMTUxLjAzNyAxNTkuMjY0IDE1MC44NyAxNTkuMjA3IDE1MC43MzJDMTU5LjE1IDE1MC41OTQgMTU5LjA3NyAxNTAuNDgyIDE1OC45ODggMTUwLjM5NkMxNTguOTAyIDE1MC4zMTEgMTU4LjgwMiAxNTAuMjQ4IDE1OC42ODcgMTUwLjIwOUMxNTguNTc1IDE1MC4xNjcgMTU4LjQ0OSAxNTAuMTQ2IDE1OC4zMDggMTUwLjE0NkMxNTguMTM2IDE1MC4xNDYgMTU3Ljk4NCAxNTAuMTc5IDE1Ny44NTEgMTUwLjI0NEMxNTcuNzE5IDE1MC4zMDcgMTU3LjYwNyAxNTAuNDA3IDE1Ny41MTUgMTUwLjU0NUMxNTcuNDI3IDE1MC42ODMgMTU3LjM1OSAxNTAuODY0IDE1Ny4zMTIgMTUxLjA4OEMxNTcuMjY1IDE1MS4zMTIgMTU3LjI0MiAxNTEuNTg0IDE1Ny4yNDIgMTUxLjkwNFYxNTMuMDFDMTU3LjI0MiAxNTMuMjY1IDE1Ny4yNTYgMTUzLjQ5IDE1Ny4yODUgMTUzLjY4NkMxNTcuMzE2IDE1My44ODEgMTU3LjM2MiAxNTQuMDUgMTU3LjQyMiAxNTQuMTkzQzE1Ny40ODIgMTU0LjMzNCAxNTcuNTU0IDE1NC40NSAxNTcuNjQgMTU0LjU0MUMxNTcuNzI2IDE1NC42MzIgMTU3LjgyNSAxNTQuNyAxNTcuOTM3IDE1NC43NDRDMTU4LjA1MiAxNTQuNzg2IDE1OC4xNzggMTU0LjgwNyAxNTguMzE2IDE1NC44MDdDMTU4LjQ5MyAxNTQuODA3IDE1OC42NDggMTU0Ljc3MyAxNTguNzgxIDE1NC43MDVDMTU4LjkxNCAxNTQuNjM3IDE1OS4wMjUgMTU0LjUzMiAxNTkuMTEzIDE1NC4zODlDMTU5LjIwNCAxNTQuMjQzIDE1OS4yNzIgMTU0LjA1NyAxNTkuMzE2IDE1My44M0MxNTkuMzYgMTUzLjYwMSAxNTkuMzgzIDE1My4zMjcgMTU5LjM4MyAxNTMuMDFaTTE2Mi4wMzYgMTUyLjYxNUwxNjEuNDU3IDE1Mi40NjdMMTYxLjc0MyAxNDkuNjM1SDE2NC42NjFWMTUwLjMwM0gxNjIuMzU2TDE2Mi4xODQgMTUxLjg1QzE2Mi4yODggMTUxLjc5IDE2Mi40MiAxNTEuNzM0IDE2Mi41NzkgMTUxLjY4MkMxNjIuNzQgMTUxLjYzIDE2Mi45MjUgMTUxLjYwNCAxNjMuMTMzIDE1MS42MDRDMTYzLjM5NiAxNTEuNjA0IDE2My42MzIgMTUxLjY0OSAxNjMuODQgMTUxLjc0QzE2NC4wNDkgMTUxLjgyOSAxNjQuMjI2IDE1MS45NTYgMTY0LjM3MSAxNTIuMTIzQzE2NC41MiAxNTIuMjkgMTY0LjYzMyAxNTIuNDkgMTY0LjcxMSAxNTIuNzI1QzE2NC43ODkgMTUyLjk1OSAxNjQuODI5IDE1My4yMjEgMTY0LjgyOSAxNTMuNTFDMTY0LjgyOSAxNTMuNzgzIDE2NC43OTEgMTU0LjAzNSAxNjQuNzE1IDE1NC4yNjRDMTY0LjY0MiAxNTQuNDkzIDE2NC41MzIgMTU0LjY5MyAxNjQuMzgzIDE1NC44NjVDMTY0LjIzNSAxNTUuMDM1IDE2NC4wNDcgMTU1LjE2NiAxNjMuODIxIDE1NS4yNkMxNjMuNTk3IDE1NS4zNTQgMTYzLjMzMiAxNTUuNCAxNjMuMDI4IDE1NS40QzE2Mi43OTkgMTU1LjQgMTYyLjU4MSAxNTUuMzY5IDE2Mi4zNzUgMTU1LjMwN0MxNjIuMTcyIDE1NS4yNDIgMTYxLjk5IDE1NS4xNDQgMTYxLjgyOSAxNTUuMDE0QzE2MS42NyAxNTQuODgxIDE2MS41MzkgMTU0LjcxNyAxNjEuNDM4IDE1NC41MjFDMTYxLjMzOSAxNTQuMzI0IDE2MS4yNzYgMTU0LjA5MiAxNjEuMjUgMTUzLjgyNkgxNjEuOTM4QzE2MS45NjkgMTU0LjA0IDE2Mi4wMzIgMTU0LjIxOSAxNjIuMTI1IDE1NC4zNjVDMTYyLjIxOSAxNTQuNTExIDE2Mi4zNDIgMTU0LjYyMiAxNjIuNDkzIDE1NC42OTdDMTYyLjY0NiAxNTQuNzcgMTYyLjgyNSAxNTQuODA3IDE2My4wMjggMTU0LjgwN0MxNjMuMiAxNTQuODA3IDE2My4zNTIgMTU0Ljc3NyAxNjMuNDg1IDE1NC43MTdDMTYzLjYxOCAxNTQuNjU3IDE2My43MyAxNTQuNTcxIDE2My44MjEgMTU0LjQ1OUMxNjMuOTEyIDE1NC4zNDcgMTYzLjk4MSAxNTQuMjEyIDE2NC4wMjggMTU0LjA1M0MxNjQuMDc3IDE1My44OTQgMTY0LjEwMiAxNTMuNzE1IDE2NC4xMDIgMTUzLjUxOEMxNjQuMTAyIDE1My4zMzggMTY0LjA3NyAxNTMuMTcxIDE2NC4wMjggMTUzLjAxOEMxNjMuOTc4IDE1Mi44NjQgMTYzLjkwNCAxNTIuNzMgMTYzLjgwNSAxNTIuNjE1QzE2My43MDkgMTUyLjUwMSAxNjMuNTkgMTUyLjQxMiAxNjMuNDUgMTUyLjM1QzE2My4zMDkgMTUyLjI4NSAxNjMuMTQ4IDE1Mi4yNTIgMTYyLjk2NSAxNTIuMjUyQzE2Mi43MjMgMTUyLjI1MiAxNjIuNTM5IDE1Mi4yODUgMTYyLjQxNCAxNTIuMzVDMTYyLjI5MiAxNTIuNDE1IDE2Mi4xNjYgMTUyLjUwMyAxNjIuMDM2IDE1Mi42MTVaTTE2OC43MTMgMTQ5LjYzNVYxNTUuMzIySDE2Ny45NTlWMTQ5LjYzNUgxNjguNzEzWk0xNzEuMDk1IDE1Mi4xOTNWMTUyLjgxMUgxNjguNTQ4VjE1Mi4xOTNIMTcxLjA5NVpNMTcxLjQ4MiAxNDkuNjM1VjE1MC4yNTJIMTY4LjU0OFYxNDkuNjM1SDE3MS40ODJaTTE3NC4wMjIgMTU1LjRDMTczLjcyNyAxNTUuNCAxNzMuNDYgMTU1LjM1MSAxNzMuMjIxIDE1NS4yNTJDMTcyLjk4NCAxNTUuMTUgMTcyLjc4IDE1NS4wMDggMTcyLjYwOCAxNTQuODI2QzE3Mi40MzggMTU0LjY0NCAxNzIuMzA4IDE1NC40MjggMTcyLjIxNyAxNTQuMTc4QzE3Mi4xMjYgMTUzLjkyOCAxNzIuMDggMTUzLjY1NCAxNzIuMDggMTUzLjM1N1YxNTMuMTkzQzE3Mi4wOCAxNTIuODUgMTcyLjEzMSAxNTIuNTQ0IDE3Mi4yMzMgMTUyLjI3NUMxNzIuMzM0IDE1Mi4wMDUgMTcyLjQ3MiAxNTEuNzc1IDE3Mi42NDcgMTUxLjU4OEMxNzIuODIxIDE1MS40IDE3My4wMTkgMTUxLjI1OCAxNzMuMjQgMTUxLjE2MkMxNzMuNDYyIDE1MS4wNjYgMTczLjY5MSAxNTEuMDE4IDE3My45MjggMTUxLjAxOEMxNzQuMjMgMTUxLjAxOCAxNzQuNDkgMTUxLjA3IDE3NC43MDkgMTUxLjE3NEMxNzQuOTMxIDE1MS4yNzggMTc1LjExMiAxNTEuNDI0IDE3NS4yNTIgMTUxLjYxMUMxNzUuMzkzIDE1MS43OTYgMTc1LjQ5NyAxNTIuMDE1IDE3NS41NjUgMTUyLjI2OEMxNzUuNjMyIDE1Mi41MTggMTc1LjY2NiAxNTIuNzkxIDE3NS42NjYgMTUzLjA4OFYxNTMuNDEySDE3Mi41MVYxNTIuODIySDE3NC45NDRWMTUyLjc2OEMxNzQuOTMzIDE1Mi41OCAxNzQuODk0IDE1Mi4zOTggMTc0LjgyNiAxNTIuMjIxQzE3NC43NjEgMTUyLjA0NCAxNzQuNjU3IDE1MS44OTggMTc0LjUxNCAxNTEuNzgzQzE3NC4zNzEgMTUxLjY2OSAxNzQuMTc1IDE1MS42MTEgMTczLjkyOCAxNTEuNjExQzE3My43NjQgMTUxLjYxMSAxNzMuNjEzIDE1MS42NDYgMTczLjQ3NSAxNTEuNzE3QzE3My4zMzcgMTUxLjc4NSAxNzMuMjE4IDE1MS44ODYgMTczLjExOSAxNTIuMDIxQzE3My4wMiAxNTIuMTU3IDE3Mi45NDQgMTUyLjMyMiAxNzIuODg5IDE1Mi41MThDMTcyLjgzNCAxNTIuNzEzIDE3Mi44MDcgMTUyLjkzOCAxNzIuODA3IDE1My4xOTNWMTUzLjM1N0MxNzIuODA3IDE1My41NTggMTcyLjgzNCAxNTMuNzQ3IDE3Mi44ODkgMTUzLjkyNEMxNzIuOTQ2IDE1NC4wOTggMTczLjAyOCAxNTQuMjUyIDE3My4xMzUgMTU0LjM4NUMxNzMuMjQ0IDE1NC41MTggMTczLjM3NiAxNTQuNjIyIDE3My41MyAxNTQuNjk3QzE3My42ODYgMTU0Ljc3MyAxNzMuODYzIDE1NC44MTEgMTc0LjA2MSAxNTQuODExQzE3NC4zMTYgMTU0LjgxMSAxNzQuNTMyIDE1NC43NTggMTc0LjcwOSAxNTQuNjU0QzE3NC44ODYgMTU0LjU1IDE3NS4wNDEgMTU0LjQxMSAxNzUuMTc0IDE1NC4yMzZMMTc1LjYxMiAxNTQuNTg0QzE3NS41MiAxNTQuNzIyIDE3NS40MDUgMTU0Ljg1NCAxNzUuMjY0IDE1NC45NzlDMTc1LjEyMyAxNTUuMTA0IDE3NC45NSAxNTUuMjA1IDE3NC43NDQgMTU1LjI4M0MxNzQuNTQxIDE1NS4zNjEgMTc0LjMgMTU1LjQgMTc0LjAyMiAxNTUuNFpNMTc2LjU4OSAxNDkuMzIySDE3Ny4zMTVWMTU0LjUwMkwxNzcuMjUzIDE1NS4zMjJIMTc2LjU4OVYxNDkuMzIyWk0xODAuMTcxIDE1My4xNzRWMTUzLjI1NkMxODAuMTcxIDE1My41NjMgMTgwLjEzNCAxNTMuODQ4IDE4MC4wNjEgMTU0LjExMUMxNzkuOTg4IDE1NC4zNzIgMTc5Ljg4MiAxNTQuNTk4IDE3OS43NDEgMTU0Ljc5MUMxNzkuNiAxNTQuOTg0IDE3OS40MjkgMTU1LjEzMyAxNzkuMjI1IDE1NS4yNEMxNzkuMDIyIDE1NS4zNDcgMTc4Ljc4OSAxNTUuNCAxNzguNTI2IDE1NS40QzE3OC4yNTggMTU1LjQgMTc4LjAyMiAxNTUuMzU1IDE3Ny44MTkgMTU1LjI2NEMxNzcuNjE5IDE1NS4xNyAxNzcuNDQ5IDE1NS4wMzYgMTc3LjMxMSAxNTQuODYxQzE3Ny4xNzMgMTU0LjY4NyAxNzcuMDYzIDE1NC40NzYgMTc2Ljk3OSAxNTQuMjI5QzE3Ni44OTkgMTUzLjk4MSAxNzYuODQzIDE1My43MDIgMTc2LjgxMSAxNTMuMzkzVjE1My4wMzNDMTc2Ljg0MyAxNTIuNzIxIDE3Ni44OTkgMTUyLjQ0MSAxNzYuOTc5IDE1Mi4xOTNDMTc3LjA2MyAxNTEuOTQ2IDE3Ny4xNzMgMTUxLjczNSAxNzcuMzExIDE1MS41NjFDMTc3LjQ0OSAxNTEuMzgzIDE3Ny42MTkgMTUxLjI0OSAxNzcuODE5IDE1MS4xNThDMTc4LjAyIDE1MS4wNjQgMTc4LjI1MyAxNTEuMDE4IDE3OC41MTggMTUxLjAxOEMxNzguNzg0IDE1MS4wMTggMTc5LjAyIDE1MS4wNyAxNzkuMjI1IDE1MS4xNzRDMTc5LjQzMSAxNTEuMjc1IDE3OS42MDMgMTUxLjQyMSAxNzkuNzQxIDE1MS42MTFDMTc5Ljg4MiAxNTEuODAxIDE3OS45ODggMTUyLjAyOSAxODAuMDYxIDE1Mi4yOTVDMTgwLjEzNCAxNTIuNTU4IDE4MC4xNzEgMTUyLjg1MSAxODAuMTcxIDE1My4xNzRaTTE3OS40NDQgMTUzLjI1NlYxNTMuMTc0QzE3OS40NDQgMTUyLjk2MyAxNzkuNDI1IDE1Mi43NjUgMTc5LjM4NiAxNTIuNThDMTc5LjM0NyAxNTIuMzkzIDE3OS4yODQgMTUyLjIyOSAxNzkuMTk4IDE1Mi4wODhDMTc5LjExMiAxNTEuOTQ1IDE3OC45OTkgMTUxLjgzMyAxNzguODU4IDE1MS43NTJDMTc4LjcxOCAxNTEuNjY5IDE3OC41NDQgMTUxLjYyNyAxNzguMzM5IDE1MS42MjdDMTc4LjE1NiAxNTEuNjI3IDE3Ny45OTggMTUxLjY1OCAxNzcuODYyIDE1MS43MjFDMTc3LjcyOSAxNTEuNzgzIDE3Ny42MTYgMTUxLjg2OCAxNzcuNTIyIDE1MS45NzVDMTc3LjQyOSAxNTIuMDc5IDE3Ny4zNTIgMTUyLjE5OSAxNzcuMjkyIDE1Mi4zMzRDMTc3LjIzNSAxNTIuNDY3IDE3Ny4xOTIgMTUyLjYwNSAxNzcuMTYzIDE1Mi43NDhWMTUzLjY4OUMxNzcuMjA1IDE1My44NzIgMTc3LjI3MiAxNTQuMDQ4IDE3Ny4zNjYgMTU0LjIxN0MxNzcuNDYyIDE1NC4zODMgMTc3LjU5IDE1NC41MiAxNzcuNzQ5IDE1NC42MjdDMTc3LjkxIDE1NC43MzQgMTc4LjExIDE1NC43ODcgMTc4LjM0NyAxNTQuNzg3QzE3OC41NDIgMTU0Ljc4NyAxNzguNzA4IDE1NC43NDggMTc4Ljg0NyAxNTQuNjdDMTc4Ljk4NyAxNTQuNTg5IDE3OS4xIDE1NC40NzkgMTc5LjE4NiAxNTQuMzM4QzE3OS4yNzUgMTU0LjE5NyAxNzkuMzQgMTU0LjAzNSAxNzkuMzgyIDE1My44NUMxNzkuNDIzIDE1My42NjUgMTc5LjQ0NCAxNTMuNDY3IDE3OS40NDQgMTUzLjI1NloiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPC9nPgo8cGF0aCBkPSJNMTQuNSAxNy41SDI3SDYwLjVDNjEuMDUyMyAxNy41IDYxLjUgMTcuOTQ3NyA2MS41IDE4LjVWMjZDNjEuNSAyNi41NTIzIDYxLjk0NzcgMjcgNjIuNSAyN0g5NkM5Ni41NTIzIDI3IDk3IDI2LjU1MjMgOTcgMjZWMTIuNUM5NyAxMS45NDc3IDk3LjQ0NzcgMTEuNSA5OCAxMS41SDEzMkMxMzIuNTUyIDExLjUgMTMzIDExLjk0NzcgMTMzIDEyLjVWMzIuNUMxMzMgMzMuMDUyMyAxMzMuNDQ4IDMzLjUgMTM0IDMzLjVIMTY3LjVDMTY4LjA1MiAzMy41IDE2OC41IDMzLjA1MjMgMTY4LjUgMzIuNVYyNEMxNjguNSAyMy40NDc3IDE2OC45NDggMjMgMTY5LjUgMjNIMTg2IiBzdHJva2U9IiNGRkMxMDciIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxwYXRoIGQ9Ik0xMSAxMTdIMjdWMTI3LjVINjJWNzkuMTYxMUg5N1YxMDFIMTMyVjg5LjI2NzVIMTY4LjVWMTIxLjVIMTg2IiBzdHJva2U9IiM0Q0FGNTAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxwYXRoIGQ9Ik0xMiA2MC41SDI4VjY5LjVINjIuNVY0N0w5NyA0Ny4wMDAyVjU3LjAwMDJMMTMzIDU3VjY5LjVIMTY4SDE4NS41IiBzdHJva2U9IiMyMTk2RjMiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjwvZz4KPGRlZnM+CjxjbGlwUGF0aCBpZD0iY2xpcDBfNDQzN185Njg1OCI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8Y2xpcFBhdGggaWQ9ImNsaXAxXzQ0MzdfOTY4NTgiPgo8cmVjdCB3aWR0aD0iMzUuNCIgaGVpZ2h0PSIxMC4zMjIiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg0NC4zOTk5IDE0NikiLz4KPC9jbGlwUGF0aD4KPGNsaXBQYXRoIGlkPSJjbGlwMl80NDM3Xzk2ODU4Ij4KPHJlY3Qgd2lkdGg9IjM1LjQiIGhlaWdodD0iMTAuMzIyIiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoNzkuNzk5OCAxNDYpIi8+CjwvY2xpcFBhdGg+CjxjbGlwUGF0aCBpZD0iY2xpcDNfNDQzN185Njg1OCI+CjxyZWN0IHdpZHRoPSIzNS40IiBoZWlnaHQ9IjEwLjMyMiIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDExNS4yIDE0NikiLz4KPC9jbGlwUGF0aD4KPGNsaXBQYXRoIGlkPSJjbGlwNF80NDM3Xzk2ODU4Ij4KPHJlY3Qgd2lkdGg9IjM1LjQiIGhlaWdodD0iMTAuMzIyIiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTUwLjYgMTQ2KSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo=",
"description": "Displays changes to the state of the entity over time. For example, online and offline.",
"descriptor": {
"type": "timeseries",
"sizeX": 8,
"sizeY": 5,
"resources": [],
- "templateHtml": "\n",
- "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n",
- "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.flotWidget.onDataUpdated();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.flotWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.flotWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.flotWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.flotWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries', units: '°C', decimals: 0 }];\n }\n };\n}\n\n",
+ "templateHtml": "\n",
+ "templateCss": "",
+ "controllerScript": "self.onInit = function() {\n self.ctx.$scope.timeSeriesChartWidget.onInit();\n};\n\nself.onDataUpdated = function() {\n self.ctx.$scope.timeSeriesChartWidget.onDataUpdated();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.timeSeriesChartWidget.onLatestDataUpdated();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true,\n chartType: 'state',\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n dataKeySettingsFunction: TbTimeSeriesChart.dataKeySettings('state'),\n defaultDataKeysFunction: function() {\n return [{ name: 'state', label: 'State', type: 'timeseries', units: '', decimals: 0 }];\n }\n };\n}\n",
"settingsSchema": "{}",
"dataKeySettingsSchema": "{}",
- "settingsDirective": "tb-flot-line-widget-settings",
- "dataKeySettingsDirective": "tb-flot-line-key-settings",
- "latestDataKeySettingsDirective": "tb-flot-latest-key-settings",
+ "latestDataKeySettingsSchema": "{}",
+ "settingsDirective": "tb-time-series-chart-widget-settings",
+ "dataKeySettingsDirective": "tb-time-series-chart-key-settings",
+ "latestDataKeySettingsDirective": "",
"hasBasicMode": true,
- "basicModeDirective": "tb-flot-basic-config",
- "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":0,\"max\":1.2,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"timeForComparison\":\"previousInterval\",\"comparisonCustomIntervalValue\":7200000,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"right\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"dataKeysListForLabels\":[]},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"configMode\":\"basic\",\"showTitleIcon\":false,\"titleIcon\":\"waterfall_chart\",\"iconColor\":\"#1F6BDD\"}"
+ "basicModeDirective": "tb-time-series-chart-basic-config",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"yAxisId\":\"default\",\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":true,\"step\":true,\"stepType\":\"end\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":false,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"enablePointLabelBackground\":false,\"pointLabelBackground\":\"rgba(255,255,255,0.56)\",\"pointShape\":\"circle\",\"pointSize\":12,\"fillAreaSettings\":{\"type\":\"opacity\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"barSettings\":{\"showBorder\":false,\"borderWidth\":2,\"borderRadius\":0,\"showLabel\":false,\"labelPosition\":\"top\",\"labelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.76)\",\"enableLabelBackground\":false,\"labelBackground\":\"rgba(255,255,255,0.56)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"tooltipValueFormatter\":\"\"},\"_hash\":0.676226248393859,\"funcBody\":\"return Math.random() > 0.5 ? true : false;\",\"decimals\":0,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null,\"units\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#FFC107\",\"settings\":{\"yAxisId\":\"default\",\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":true,\"step\":true,\"stepType\":\"end\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":false,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"enablePointLabelBackground\":false,\"pointLabelBackground\":\"rgba(255,255,255,0.56)\",\"pointShape\":\"circle\",\"pointSize\":12,\"fillAreaSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"barSettings\":{\"showBorder\":false,\"borderWidth\":2,\"borderRadius\":0,\"showLabel\":false,\"labelPosition\":\"top\",\"labelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.76)\",\"enableLabelBackground\":false,\"labelBackground\":\"rgba(255,255,255,0.56)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}},\"tooltipValueFormatter\":null},\"_hash\":0.1106990458957191,\"funcBody\":\"return Math.random() <= 0.5 ? true : false;\",\"decimals\":0,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":null,\"units\":null}],\"alarmFilterConfig\":{\"statusList\":[\"ACTIVE\"]},\"latestDataKeys\":null}],\"timewindow\":{\"hideInterval\":false,\"hideLastInterval\":false,\"hideQuickInterval\":false,\"hideAggregation\":false,\"hideAggInterval\":false,\"hideTimezone\":false,\"selectedTab\":0,\"realtime\":{\"realtimeType\":0,\"timewindowMs\":60000,\"quickInterval\":\"CURRENT_DAY\",\"interval\":1000},\"aggregation\":{\"type\":\"NONE\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"yAxes\":{\"default\":{\"units\":null,\"decimals\":0,\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"left\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormatter\":\"\",\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\",\"id\":\"default\",\"order\":0,\"interval\":null,\"splitNumber\":null,\"min\":null,\"max\":null,\"ticksGenerator\":\"\"}},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"xAxis\":{\"show\":true,\"label\":\"\",\"labelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"600\",\"lineHeight\":\"1\"},\"labelColor\":\"rgba(0, 0, 0, 0.54)\",\"position\":\"bottom\",\"showTickLabels\":true,\"tickLabelFont\":{\"family\":\"Roboto\",\"size\":10,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"tickLabelColor\":\"rgba(0, 0, 0, 0.54)\",\"ticksFormat\":{},\"showTicks\":true,\"ticksColor\":\"rgba(0, 0, 0, 0.54)\",\"showLine\":true,\"lineColor\":\"rgba(0, 0, 0, 0.54)\",\"showSplitLines\":true,\"splitLinesColor\":\"rgba(0, 0, 0, 0.12)\"},\"noAggregationBarWidthSettings\":{\"strategy\":\"group\",\"groupWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000},\"barWidth\":{\"relative\":true,\"relativeWidth\":2,\"absoluteWidth\":1000}},\"showLegend\":true,\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"legendConfig\":{\"direction\":\"column\",\"position\":\"right\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false,\"showLatest\":false},\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipValueFormatter\":\"\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":null,\"lastUpdateAgo\":false,\"custom\":false,\"auto\":true,\"autoDateFormatSettings\":{\"millisecond\":\"MMM dd yyyy HH:mm:ss\"}},\"tooltipDateFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"tooltipDateColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipDateInterval\":true,\"tooltipBackgroundColor\":\"rgba(255, 255, 255, 0.76)\",\"tooltipBackgroundBlur\":4,\"animation\":{\"animation\":true,\"animationThreshold\":2000,\"animationDuration\":500,\"animationEasing\":\"cubicOut\",\"animationDelay\":0,\"animationDurationUpdate\":300,\"animationEasingUpdate\":\"cubicOut\",\"animationDelayUpdate\":0},\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}},\"padding\":\"12px\",\"states\":[{\"label\":\"Off\",\"value\":0,\"sourceType\":\"constant\",\"sourceValue\":false},{\"label\":\"On\",\"value\":1,\"sourceType\":\"constant\",\"sourceValue\":true}]},\"title\":\"State chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":null,\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\",\"useDashboardTimewindow\":false,\"displayTimewindow\":true,\"titleFont\":{\"size\":16,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"500\",\"style\":\"normal\",\"lineHeight\":\"24px\"},\"titleColor\":\"rgba(0, 0, 0, 0.87)\",\"titleTooltip\":\"\",\"widgetStyle\":{},\"widgetCss\":\"\",\"pageSize\":1024,\"units\":\"\",\"decimals\":null,\"noDataDisplayMessage\":\"\",\"timewindowStyle\":{\"showIcon\":false,\"iconSize\":\"24px\",\"icon\":null,\"iconPosition\":\"left\",\"font\":{\"size\":12,\"sizeUnit\":\"px\",\"family\":\"Roboto\",\"weight\":\"400\",\"style\":\"normal\",\"lineHeight\":\"16px\"},\"color\":\"rgba(0, 0, 0, 0.38)\",\"displayTypePrefix\":true},\"margin\":\"0px\",\"borderRadius\":\"0px\",\"iconSize\":\"0px\"}"
},
- "externalId": null,
- "tags": null
+ "tags": [
+ "chart",
+ "time series",
+ "time-series",
+ "state",
+ "state chart",
+ "online",
+ "offline"
+ ]
}
\ No newline at end of file
diff --git a/application/src/main/data/json/system/widget_types/state_chart_deprecated.json b/application/src/main/data/json/system/widget_types/state_chart_deprecated.json
new file mode 100644
index 0000000000..90fc886d62
--- /dev/null
+++ b/application/src/main/data/json/system/widget_types/state_chart_deprecated.json
@@ -0,0 +1,25 @@
+{
+ "fqn": "charts.state_chart",
+ "name": "State Chart",
+ "deprecated": true,
+ "image": "tb-image:c3RhdGVfY2hhcnRfc3lzdGVtX3dpZGdldF9pbWFnZS5wbmc=:IlN0YXRlIENoYXJ0IiBzeXN0ZW0gd2lkZ2V0IGltYWdl;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAB9VBMVEUAAAAhlvMilvMymeE+nNVDoetInslNq/VZqOdpuPd3d3d5p5Z5suB6enp8fHyBgYGDg4ODqoqEhISIiIiKioqKuN2MjIyNjY2Ojo6QkJCRkZGSkpKUlJSVlZWWlpaXl5eYmJiZmZmampqcnJydnZ2enp6goKCgr3KhoaGioqKisXSjo6OjvsmkpKSlpaWlwdempqanp6eoqKiosGOo1vqpqampsGOqqqqqsWOrq6usrKysw9Wurq6wsLCysrK0tLS1tbW1wbK2tra3t7e4wau5ubm6urq7u7u8vLy9vb2+vr6/xbTAwMDAxbjCwsLC3ejDw8PExMTFxcXGxsbHx8fIv3jIyMjJycnKysrK5vzMzc7Nzc3Ozs7Pz8/QuDnRzcHS0tLT09PT39HU1NTU6/3VzLLV1dXV3sjW1tbX19fYuTHY2NjZ2dna0Ira2trb29vc3Nzex4De3t7f39/guyvhvCfhvCvh4eHh6Nbi4uLjvCTj4+PkyXbk5OTlvCTm5ubm693o6Ojpxl7q6urr6+vswTTs7Ozt7e3uvhju7u7vvhjv7+/wvxjw8PDx8fHyvxX0yTv09PT19fX29vb39/f4wyL4+Pj5+fn6+vr75J37+/v8whP8/Pz9/f39/v/+/v7/wQf/xRb/3HT/5JH/9tz/++/////APs7XAAAAAWJLR0Smt7AblQAAA1RJREFUeNrt3dlT01AUBvAEd8UNl2oLrdrFolZArUul1hWlVhQXFAUF1xaxIqi4FUQUrDsUClZi4nb+Th96S9NQkjDOOKZ+3wuZw8md++M25OXMlKOccJQnTUYocdRqdw8UAqTfOjG4lvr7uowOqbtAtG6o5OiGFoNDapuJHO8tFK4xOKS9msTVgoUiaQjPclnSl01snZ/y4ly2yBNZbRarbc+3yvdpt/hLawOPJp9ur7O0mRiEmzFkY1M6N/4I8qJpulzR2sBx1sgRjQuyA2pk+STdylw2VqR/FPFfWdcCvjhduiM7kVF2db1Nuspu7JGes+I76SRba7f0Wfnn/6F6It+mfo7m8TvYvh7KTiT3PQIIIIAAAggggAACCCCAAAIIIIAAAggggAACCCCAAAIIIIAAAggggPwXkFa7e7AQIP3WiUFzdl7LuBDFvJZxIYp5LeNCFPNaBn7Yc+e1KlheSYcrFCniL7LZqPl8cbp0TjavNcqu9rZJB9gdPdIJVnwjbWG1ndI95UzWM9V5rS9Ti3P4VWy1+5jXAgQQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBAMK+FeS3Ma2FeC/NamNfCCxEQQAABBBBAAAEEEEAAAQQQQAABBBBAAAEEEEAAAQQQQAABBBBA/hZkTKSkwSGn7VUJOmOLV241NiRSRZ2uYcvt+Io+Y0N83USm+MqGWEnU2JCqXqI1ZE+QhYiIy36tXR5INpOQbB5kftcmK85mtdey2iJekeWqX6/3kc+TLCQTrq6eEuZCgKTsbnNXBsIZOERJMedNQnry73XpW8UAKVhIyBPScVfQE9RqEaP7iMT6g+pdw7vKbxKl3IJqV8K7OUqXPHvG9UMGykk2lz1d4g5yvNXo6QqZiA41aHT5OwQTUWBJSrWrvlMwj1jFU2f1Q8K1dCysCUmVhdenNLvMRKZt3hGNriEnxfxlGqt9aPYRkb9bP6Q1SMFrmltMukKuMT2QpckWjc/WhP2lYB/TgjzdHyCKVM/gGXnsp+qY5hbba+hIVA/ETL0+9SepsoNiTs/igGpXZIhKqVv9QVJARIfXIWqfiM1nS+qBnHdae1V7Qss8ngEijRO5a6sMCAtdqv+HfgPwpNPbU6ipOwAAAABJRU5ErkJggg==",
+ "description": "Displays changes to the state of the entity over time. For example, online and offline.",
+ "descriptor": {
+ "type": "timeseries",
+ "sizeX": 8,
+ "sizeY": 5,
+ "resources": [],
+ "templateHtml": "\n",
+ "templateCss": ".legend {\n font-size: 13px;\n line-height: 10px;\n}\n\n.legend table { \n border-spacing: 0px;\n border-collapse: separate;\n}\n\n.mouse-events .flot-overlay {\n cursor: crosshair; \n}\n\n",
+ "controllerScript": "self.onInit = function() {\n}\n\nself.onDataUpdated = function() {\n self.ctx.$scope.flotWidget.onDataUpdated();\n}\n\nself.onLatestDataUpdated = function() {\n self.ctx.$scope.flotWidget.onLatestDataUpdated();\n}\n\nself.onResize = function() {\n self.ctx.$scope.flotWidget.onResize();\n}\n\nself.onEditModeChanged = function() {\n self.ctx.$scope.flotWidget.onEditModeChanged();\n}\n\nself.onDestroy = function() {\n self.ctx.$scope.flotWidget.onDestroy();\n}\n\nself.typeParameters = function() {\n return {\n stateData: true,\n hasAdditionalLatestDataKeys: true,\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries', units: '°C', decimals: 0 }];\n }\n };\n}\n\n",
+ "settingsSchema": "{}",
+ "dataKeySettingsSchema": "{}",
+ "settingsDirective": "tb-flot-line-widget-settings",
+ "dataKeySettingsDirective": "tb-flot-line-key-settings",
+ "latestDataKeySettingsDirective": "tb-flot-latest-key-settings",
+ "hasBasicMode": true,
+ "basicModeDirective": "tb-flot-basic-config",
+ "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 1\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false,\"axisPosition\":\"left\",\"showSeparateAxis\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"return Math.random() > 0.5 ? 1 : 0;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Switch 2\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false,\"axisPosition\":\"left\"},\"_hash\":0.12775350966079668,\"funcBody\":\"return Math.random() <= 0.5 ? 1 : 0;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"stack\":false,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"tooltipValueFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\",\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":0,\"max\":1.2,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"if (value > 0 && value <= 1) {\\n return 'On';\\n} else if (value === 0) {\\n return 'Off';\\n} else {\\n return '';\\n}\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"timeForComparison\":\"previousInterval\",\"comparisonCustomIntervalValue\":7200000,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"right\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":false,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false,\"dataKeysListForLabels\":[]},\"title\":\"State Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"configMode\":\"basic\",\"showTitleIcon\":false,\"titleIcon\":\"waterfall_chart\",\"iconColor\":\"#1F6BDD\"}"
+ },
+ "tags": null
+}
\ No newline at end of file
diff --git a/application/src/main/data/upgrade/3.6.3/schema_update.sql b/application/src/main/data/upgrade/3.6.3/schema_update.sql
index e2d454353e..3e0fdc3b4b 100644
--- a/application/src/main/data/upgrade/3.6.3/schema_update.sql
+++ b/application/src/main/data/upgrade/3.6.3/schema_update.sql
@@ -14,95 +14,14 @@
-- limitations under the License.
--
+-- NOTIFICATIONS UPDATE START
--- create new attribute_kv table schema
-DO
-$$
- BEGIN
- -- in case of running the upgrade script a second time:
- IF EXISTS(SELECT 1 FROM information_schema.columns WHERE table_name = 'attribute_kv' and column_name='entity_type') THEN
- DROP VIEW IF EXISTS device_info_view;
- DROP VIEW IF EXISTS device_info_active_attribute_view;
- ALTER INDEX IF EXISTS idx_attribute_kv_by_key_and_last_update_ts RENAME TO idx_attribute_kv_by_key_and_last_update_ts_old;
- IF EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'attribute_kv_pkey') THEN
- ALTER TABLE attribute_kv RENAME CONSTRAINT attribute_kv_pkey TO attribute_kv_pkey_old;
- END IF;
- ALTER TABLE attribute_kv RENAME TO attribute_kv_old;
- CREATE TABLE IF NOT EXISTS attribute_kv
- (
- entity_id uuid,
- attribute_type int,
- attribute_key int,
- bool_v boolean,
- str_v varchar(10000000),
- long_v bigint,
- dbl_v double precision,
- json_v json,
- last_update_ts bigint,
- CONSTRAINT attribute_kv_pkey PRIMARY KEY (entity_id, attribute_type, attribute_key)
- );
- END IF;
- END;
-$$;
+ALTER TABLE notification ADD COLUMN IF NOT EXISTS delivery_method VARCHAR(50) NOT NULL default 'WEB';
--- rename ts_kv_dictionary table to key_dictionary or create table if not exists
-DO
-$$
- BEGIN
- IF EXISTS(SELECT 1 FROM information_schema.tables WHERE table_name = 'ts_kv_dictionary') THEN
- ALTER TABLE ts_kv_dictionary RENAME CONSTRAINT ts_key_id_pkey TO key_dictionary_id_pkey;
- ALTER TABLE ts_kv_dictionary RENAME TO key_dictionary;
- ELSE CREATE TABLE IF NOT EXISTS key_dictionary(
- key varchar(255) NOT NULL,
- key_id serial UNIQUE,
- CONSTRAINT key_dictionary_id_pkey PRIMARY KEY (key)
- );
- END IF;
- END;
-$$;
+DROP INDEX IF EXISTS idx_notification_recipient_id_created_time;
+DROP INDEX IF EXISTS idx_notification_recipient_id_unread;
--- insert keys into key_dictionary
-DO
-$$
- BEGIN
- IF EXISTS(SELECT 1 FROM information_schema.tables WHERE table_name = 'attribute_kv_old') THEN
- INSERT INTO key_dictionary(key) SELECT DISTINCT attribute_key FROM attribute_kv_old ON CONFLICT DO NOTHING;
- END IF;
- END;
-$$;
+CREATE INDEX IF NOT EXISTS idx_notification_delivery_method_recipient_id_created_time ON notification(delivery_method, recipient_id, created_time DESC);
+CREATE INDEX IF NOT EXISTS idx_notification_delivery_method_recipient_id_unread ON notification(delivery_method, recipient_id) WHERE status <> 'READ';
--- migrate attributes from attribute_kv_old to attribute_kv
-DO
-$$
-DECLARE
- row_num_old integer;
- row_num integer;
-BEGIN
- IF EXISTS(SELECT 1 FROM information_schema.tables WHERE table_name = 'attribute_kv_old') THEN
- INSERT INTO attribute_kv(entity_id, attribute_type, attribute_key, bool_v, str_v, long_v, dbl_v, json_v, last_update_ts)
- SELECT a.entity_id, CASE
- WHEN a.attribute_type = 'CLIENT_SCOPE' THEN 1
- WHEN a.attribute_type = 'SERVER_SCOPE' THEN 2
- WHEN a.attribute_type = 'SHARED_SCOPE' THEN 3
- ELSE 0
- END,
- k.key_id, a.bool_v, a.str_v, a.long_v, a.dbl_v, a.json_v, a.last_update_ts
- FROM attribute_kv_old a INNER JOIN key_dictionary k ON (a.attribute_key = k.key);
- SELECT COUNT(*) INTO row_num_old FROM attribute_kv_old;
- SELECT COUNT(*) INTO row_num FROM attribute_kv;
- RAISE NOTICE 'Migrated % of % rows', row_num, row_num_old;
-
- IF row_num != 0 THEN
- DROP TABLE IF EXISTS attribute_kv_old;
- ELSE
- RAISE EXCEPTION 'Table attribute_kv is empty';
- END IF;
-
- CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribute_kv(entity_id, attribute_key, last_update_ts desc);
- END IF;
-EXCEPTION
- WHEN others THEN
- ROLLBACK;
- RAISE EXCEPTION 'Error during COPY: %', SQLERRM;
-END
-$$;
\ No newline at end of file
+-- NOTIFICATIONS UPDATE END
diff --git a/application/src/main/data/upgrade/3.6.4/schema_update.sql b/application/src/main/data/upgrade/3.6.4/schema_update.sql
new file mode 100644
index 0000000000..d7d8887d13
--- /dev/null
+++ b/application/src/main/data/upgrade/3.6.4/schema_update.sql
@@ -0,0 +1,136 @@
+--
+-- Copyright © 2016-2024 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 new attribute_kv table schema
+DO
+$$
+ BEGIN
+ -- in case of running the upgrade script a second time:
+ IF EXISTS(SELECT 1 FROM information_schema.columns WHERE table_name = 'attribute_kv' and column_name='entity_type') THEN
+ DROP VIEW IF EXISTS device_info_view;
+ DROP VIEW IF EXISTS device_info_active_attribute_view;
+ ALTER INDEX IF EXISTS idx_attribute_kv_by_key_and_last_update_ts RENAME TO idx_attribute_kv_by_key_and_last_update_ts_old;
+ IF EXISTS(SELECT 1 FROM pg_constraint WHERE conname = 'attribute_kv_pkey') THEN
+ ALTER TABLE attribute_kv RENAME CONSTRAINT attribute_kv_pkey TO attribute_kv_pkey_old;
+ END IF;
+ ALTER TABLE attribute_kv RENAME TO attribute_kv_old;
+ CREATE TABLE IF NOT EXISTS attribute_kv
+ (
+ entity_id uuid,
+ attribute_type int,
+ attribute_key int,
+ bool_v boolean,
+ str_v varchar(10000000),
+ long_v bigint,
+ dbl_v double precision,
+ json_v json,
+ last_update_ts bigint,
+ CONSTRAINT attribute_kv_pkey PRIMARY KEY (entity_id, attribute_type, attribute_key)
+ );
+ END IF;
+ END;
+$$;
+
+-- rename ts_kv_dictionary table to key_dictionary or create table if not exists
+DO
+$$
+ BEGIN
+ IF EXISTS(SELECT 1 FROM information_schema.tables WHERE table_name = 'ts_kv_dictionary') THEN
+ ALTER TABLE ts_kv_dictionary RENAME CONSTRAINT ts_key_id_pkey TO key_dictionary_id_pkey;
+ ALTER TABLE ts_kv_dictionary RENAME TO key_dictionary;
+ ELSE CREATE TABLE IF NOT EXISTS key_dictionary(
+ key varchar(255) NOT NULL,
+ key_id serial UNIQUE,
+ CONSTRAINT key_dictionary_id_pkey PRIMARY KEY (key)
+ );
+ END IF;
+ END;
+$$;
+
+-- insert keys into key_dictionary
+DO
+$$
+ BEGIN
+ IF EXISTS(SELECT 1 FROM information_schema.tables WHERE table_name = 'attribute_kv_old') THEN
+ INSERT INTO key_dictionary(key) SELECT DISTINCT attribute_key FROM attribute_kv_old ON CONFLICT DO NOTHING;
+ END IF;
+ END;
+$$;
+
+-- migrate attributes from attribute_kv_old to attribute_kv
+DO
+$$
+DECLARE
+ row_num_old integer;
+ row_num integer;
+BEGIN
+ IF EXISTS(SELECT 1 FROM information_schema.tables WHERE table_name = 'attribute_kv_old') THEN
+ INSERT INTO attribute_kv(entity_id, attribute_type, attribute_key, bool_v, str_v, long_v, dbl_v, json_v, last_update_ts)
+ SELECT a.entity_id, CASE
+ WHEN a.attribute_type = 'CLIENT_SCOPE' THEN 1
+ WHEN a.attribute_type = 'SERVER_SCOPE' THEN 2
+ WHEN a.attribute_type = 'SHARED_SCOPE' THEN 3
+ ELSE 0
+ END,
+ k.key_id, a.bool_v, a.str_v, a.long_v, a.dbl_v, a.json_v, a.last_update_ts
+ FROM attribute_kv_old a INNER JOIN key_dictionary k ON (a.attribute_key = k.key);
+ SELECT COUNT(*) INTO row_num_old FROM attribute_kv_old;
+ SELECT COUNT(*) INTO row_num FROM attribute_kv;
+ RAISE NOTICE 'Migrated % of % rows', row_num, row_num_old;
+
+ IF row_num != 0 THEN
+ DROP TABLE IF EXISTS attribute_kv_old;
+ ELSE
+ RAISE EXCEPTION 'Table attribute_kv is empty';
+ END IF;
+
+ CREATE INDEX IF NOT EXISTS idx_attribute_kv_by_key_and_last_update_ts ON attribute_kv(entity_id, attribute_key, last_update_ts desc);
+ END IF;
+EXCEPTION
+ WHEN others THEN
+ ROLLBACK;
+ RAISE EXCEPTION 'Error during COPY: %', SQLERRM;
+END
+$$;
+
+-- OAUTH2 PARAMS ALTER TABLE START
+
+ALTER TABLE oauth2_params
+ ADD COLUMN IF NOT EXISTS edge_enabled boolean DEFAULT false;
+
+-- OAUTH2 PARAMS ALTER TABLE END
+
+-- QUEUE STATS UPDATE START
+
+CREATE TABLE IF NOT EXISTS queue_stats (
+ id uuid NOT NULL CONSTRAINT queue_stats_pkey PRIMARY KEY,
+ created_time bigint NOT NULL,
+ tenant_id uuid NOT NULL,
+ queue_name varchar(255) NOT NULL,
+ service_id varchar(255) NOT NULL,
+ CONSTRAINT queue_stats_name_unq_key UNIQUE (tenant_id, queue_name, service_id)
+);
+
+INSERT INTO queue_stats
+ SELECT id, created_time, tenant_id, substring(name FROM 1 FOR position('_' IN name) - 1) AS queue_name,
+ substring(name FROM position('_' IN name) + 1) AS service_id
+ FROM asset
+ WHERE type = 'TbServiceQueue' and name LIKE '%\_%';
+
+DELETE FROM asset WHERE type='TbServiceQueue';
+DELETE FROM asset_profile WHERE name ='TbServiceQueue';
+
+-- QUEUE STATS UPDATE END
diff --git a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
index 6a10791091..f411344e3f 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ActorSystemContext.java
@@ -19,6 +19,8 @@ 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.MoreExecutors;
+import jakarta.annotation.Nullable;
+import jakarta.annotation.PostConstruct;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
@@ -79,6 +81,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService;
import org.thingsboard.server.dao.notification.NotificationTemplateService;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.queue.QueueService;
+import org.thingsboard.server.dao.queue.QueueStatsService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.resource.ResourceService;
import org.thingsboard.server.dao.rule.RuleChainService;
@@ -115,9 +118,6 @@ import org.thingsboard.server.service.telemetry.AlarmSubscriptionService;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import org.thingsboard.server.service.transport.TbCoreToTransportService;
-import jakarta.annotation.Nullable;
-import jakarta.annotation.PostConstruct;
-
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.concurrent.ConcurrentHashMap;
@@ -447,6 +447,11 @@ public class ActorSystemContext {
@Getter
private QueueService queueService;
+ @Lazy
+ @Autowired(required = false)
+ @Getter
+ private QueueStatsService queueStatsService;
+
@Lazy
@Autowired(required = false)
@Getter
diff --git a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
index d6b5da4ddf..87d5c8a63a 100644
--- a/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
+++ b/application/src/main/java/org/thingsboard/server/actors/device/DeviceActorMessageProcessor.java
@@ -21,6 +21,7 @@ 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.MoreExecutors;
+import jakarta.annotation.Nullable;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.thingsboard.common.util.JacksonUtil;
@@ -42,7 +43,6 @@ import org.thingsboard.server.common.data.id.RpcId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.kv.AttributeKey;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
-import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.SortOrder;
@@ -68,14 +68,11 @@ import org.thingsboard.server.common.msg.rule.engine.DeviceEdgeUpdateMsg;
import org.thingsboard.server.common.msg.rule.engine.DeviceNameOrTypeUpdateMsg;
import org.thingsboard.server.common.msg.timeout.DeviceActorServerSideRpcTimeoutMsg;
import org.thingsboard.server.common.util.KvProtoUtil;
-import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ClaimDeviceMsg;
import org.thingsboard.server.gen.transport.TransportProtos.DeviceSessionsCacheEntry;
import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
-import org.thingsboard.server.gen.transport.TransportProtos.KeyValueProto;
-import org.thingsboard.server.gen.transport.TransportProtos.KeyValueType;
import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto;
import org.thingsboard.server.gen.transport.TransportProtos.SessionEvent;
import org.thingsboard.server.gen.transport.TransportProtos.SessionEventMsg;
@@ -97,7 +94,6 @@ import org.thingsboard.server.service.rpc.RpcSubmitStrategy;
import org.thingsboard.server.service.state.DefaultDeviceStateService;
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
-import jakarta.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -255,14 +251,12 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
}
private boolean isSendNewRpcAvailable() {
- switch (rpcSubmitStrategy) {
- case SEQUENTIAL_ON_ACK_FROM_DEVICE:
- return toDeviceRpcPendingMap.values().stream().filter(md -> !md.isDelivered()).findAny().isEmpty();
- case SEQUENTIAL_ON_RESPONSE_FROM_DEVICE:
- return toDeviceRpcPendingMap.values().stream().filter(ToDeviceRpcRequestMetadata::isDelivered).findAny().isEmpty();
- default:
- return true;
- }
+ return switch (rpcSubmitStrategy) {
+ case SEQUENTIAL_ON_ACK_FROM_DEVICE -> toDeviceRpcPendingMap.values().stream().filter(md -> !md.isDelivered()).findAny().isEmpty();
+ case SEQUENTIAL_ON_RESPONSE_FROM_DEVICE ->
+ toDeviceRpcPendingMap.values().stream().filter(ToDeviceRpcRequestMetadata::isDelivered).findAny().isEmpty();
+ default -> true;
+ };
}
private void createRpc(ToDeviceRpcRequest request, RpcStatus status) {
@@ -466,7 +460,7 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
handleSessionActivity(sessionInfo, msg.getSubscriptionInfo());
}
if (msg.hasClaimDevice()) {
- handleClaimDeviceMsg(msg.getClaimDevice());
+ handleClaimDeviceMsg(sessionInfo, msg.getClaimDevice());
}
if (msg.hasRpcResponseStatusMsg()) {
processRpcResponseStatus(sessionInfo, msg.getRpcResponseStatusMsg());
@@ -491,9 +485,22 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
});
}
- private void handleClaimDeviceMsg(ClaimDeviceMsg msg) {
+ private void handleClaimDeviceMsg(SessionInfoProto sessionInfo, ClaimDeviceMsg msg) {
+ UUID sessionId = getSessionId(sessionInfo);
DeviceId deviceId = new DeviceId(new UUID(msg.getDeviceIdMSB(), msg.getDeviceIdLSB()));
- systemContext.getClaimDevicesService().registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs());
+ ListenableFuture registrationFuture = systemContext.getClaimDevicesService()
+ .registerClaimingInfo(tenantId, deviceId, msg.getSecretKey(), msg.getDurationMs());
+ Futures.addCallback(registrationFuture, new FutureCallback<>() {
+ @Override
+ public void onSuccess(Void result) {
+ log.debug("[{}][{}] Successfully processed register claiming info request!", sessionId, deviceId);
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ log.error("[{}][{}] Failed to process register claiming info request due to: ", sessionId, deviceId, t);
+ }
+ }, MoreExecutors.directExecutor());
}
private void reportSessionOpen() {
@@ -928,34 +935,6 @@ public class DeviceActorMessageProcessor extends AbstractContextAwareMsgProcesso
return systemContext.getEdgeEventService().saveAsync(edgeEvent);
}
- private KeyValueProto toKeyValueProto(KvEntry kvEntry) {
- KeyValueProto.Builder builder = KeyValueProto.newBuilder();
- builder.setKey(kvEntry.getKey());
- switch (kvEntry.getDataType()) {
- case BOOLEAN:
- builder.setType(KeyValueType.BOOLEAN_V);
- builder.setBoolV(kvEntry.getBooleanValue().get());
- break;
- case DOUBLE:
- builder.setType(KeyValueType.DOUBLE_V);
- builder.setDoubleV(kvEntry.getDoubleValue().get());
- break;
- case LONG:
- builder.setType(KeyValueType.LONG_V);
- builder.setLongV(kvEntry.getLongValue().get());
- break;
- case STRING:
- builder.setType(KeyValueType.STRING_V);
- builder.setStringV(kvEntry.getStrValue().get());
- break;
- case JSON:
- builder.setType(KeyValueType.JSON_V);
- builder.setJsonV(kvEntry.getJsonValue().get());
- break;
- }
- return builder.build();
- }
-
void restoreSessions() {
if (systemContext.isLocalCacheType()) {
return;
diff --git a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
index 79dddfdbe4..0dc9d68984 100644
--- a/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
+++ b/application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
@@ -96,6 +96,7 @@ import org.thingsboard.server.dao.notification.NotificationTargetService;
import org.thingsboard.server.dao.notification.NotificationTemplateService;
import org.thingsboard.server.dao.ota.OtaPackageService;
import org.thingsboard.server.dao.queue.QueueService;
+import org.thingsboard.server.dao.queue.QueueStatsService;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.resource.ResourceService;
import org.thingsboard.server.dao.rule.RuleChainService;
@@ -768,6 +769,11 @@ class DefaultTbContext implements TbContext {
return mainCtx.getQueueService();
}
+ @Override
+ public QueueStatsService getQueueStatsService() {
+ return mainCtx.getQueueStatsService();
+ }
+
@Override
public EventLoopGroup getSharedEventLoop() {
return mainCtx.getSharedEventLoopGroupService().getSharedEventLoopGroup();
diff --git a/application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java b/application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java
index 93e8c0be90..3df3d6887a 100644
--- a/application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/AbstractRpcController.java
@@ -122,15 +122,9 @@ public abstract class AbstractRpcController extends BaseController {
logRpcCall(rpcRequest, rpcError, null);
RpcError error = rpcError.get();
switch (error) {
- case TIMEOUT:
- responseWriter.setResult(new ResponseEntity<>(timeoutStatus));
- break;
- case NO_ACTIVE_CONNECTION:
- responseWriter.setResult(new ResponseEntity<>(noActiveConnectionStatus));
- break;
- default:
- responseWriter.setResult(new ResponseEntity<>(timeoutStatus));
- break;
+ case TIMEOUT -> responseWriter.setResult(new ResponseEntity<>(timeoutStatus));
+ case NO_ACTIVE_CONNECTION -> responseWriter.setResult(new ResponseEntity<>(noActiveConnectionStatus));
+ default -> responseWriter.setResult(new ResponseEntity<>(timeoutStatus));
}
} else {
Optional responseData = response.getResponse();
diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
index 9c437821c6..0942329f40 100644
--- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java
@@ -506,7 +506,7 @@ public abstract class BaseController {
TenantProfile checkTenantProfileId(TenantProfileId tenantProfileId, Operation operation) throws ThingsboardException {
try {
- validateId(tenantProfileId, "Incorrect tenantProfileId " + tenantProfileId);
+ validateId(tenantProfileId, id -> "Incorrect tenantProfileId " + id);
TenantProfile tenantProfile = tenantProfileService.findTenantProfileById(getTenantId(), tenantProfileId);
checkNotNull(tenantProfile, "Tenant profile with id [" + tenantProfileId + "] is not found");
accessControlService.checkPermission(getCurrentUser(), Resource.TENANT_PROFILE, operation);
@@ -541,7 +541,7 @@ public abstract class BaseController {
if (entityId == null) {
throw new ThingsboardException("Parameter entityId can't be empty!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
}
- validateId(entityId.getId(), "Incorrect entityId " + entityId);
+ validateId(entityId.getId(), id -> "Incorrect entityId " + id);
switch (entityId.getEntityType()) {
case ALARM:
checkAlarmId(new AlarmId(entityId.getId()), operation);
@@ -668,7 +668,7 @@ public abstract class BaseController {
AlarmComment checkAlarmCommentId(AlarmCommentId alarmCommentId, AlarmId alarmId) throws ThingsboardException {
try {
- validateId(alarmCommentId, "Incorrect alarmCommentId " + alarmCommentId);
+ validateId(alarmCommentId, id -> "Incorrect alarmCommentId " + id);
AlarmComment alarmComment = alarmCommentService.findAlarmCommentByIdAsync(getCurrentUser().getTenantId(), alarmCommentId).get();
checkNotNull(alarmComment, "Alarm comment with id [" + alarmCommentId + "] is not found");
if (!alarmId.equals(alarmComment.getAlarmId())) {
@@ -736,7 +736,7 @@ public abstract class BaseController {
}
protected RuleNode checkRuleNode(RuleNodeId ruleNodeId, Operation operation) throws ThingsboardException {
- validateId(ruleNodeId, "Incorrect ruleNodeId " + ruleNodeId);
+ validateId(ruleNodeId, id -> "Incorrect ruleNodeId " + id);
RuleNode ruleNode = ruleChainService.findRuleNodeById(getTenantId(), ruleNodeId);
checkNotNull(ruleNode, "Rule node with id [" + ruleNodeId + "] is not found");
checkRuleChain(ruleNode.getRuleChainId(), operation);
diff --git a/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java b/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
index 595d6feef1..5271de2329 100644
--- a/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/ComponentDescriptorController.java
@@ -68,7 +68,7 @@ public class ComponentDescriptorController extends BaseController {
@RequestMapping(value = "/components/{componentType}", method = RequestMethod.GET)
@ResponseBody
public List getComponentDescriptorsByType(
- @Parameter(description = "Type of the Rule Node", schema = @Schema(allowableValues = "ENRICHMENT,FILTER,TRANSFORMATION,ACTION,EXTERNAL", required = true))
+ @Parameter(description = "Type of the Rule Node", schema = @Schema(allowableValues = "ENRICHMENT,FILTER,TRANSFORMATION,ACTION,EXTERNAL", requiredMode = Schema.RequiredMode.REQUIRED))
@PathVariable("componentType") String strComponentType,
@Parameter(description = "Type of the Rule Chain", schema = @Schema(allowableValues = "CORE,EDGE"))
@RequestParam(value = "ruleChainType", required = false) String strRuleChainType) throws ThingsboardException {
diff --git a/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java b/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java
index e6e57e8b8c..505244539b 100644
--- a/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/EntityQueryController.java
@@ -20,7 +20,6 @@ import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
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;
diff --git a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java
index 55f4e6d9fa..96f38daf35 100644
--- a/application/src/main/java/org/thingsboard/server/controller/NotificationController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/NotificationController.java
@@ -16,6 +16,7 @@
package org.thingsboard.server.controller;
import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -105,6 +106,8 @@ public class NotificationController extends BaseController {
private final NotificationCenter notificationCenter;
private final NotificationSettingsService notificationSettingsService;
+ private static final String DELIVERY_METHOD_ALLOWABLE_VALUES = "WEB,MOBILE_APP";
+
@ApiOperation(value = "Get notifications (getNotifications)",
notes = "Returns the page of notifications for current user." + NEW_LINE +
PAGE_DATA_PARAMETERS +
@@ -172,10 +175,23 @@ public class NotificationController extends BaseController {
@RequestParam(required = false) String sortOrder,
@Parameter(description = "To search for unread notifications only")
@RequestParam(defaultValue = "false") boolean unreadOnly,
+ @Parameter(description = "Delivery method", schema = @Schema(allowableValues = {DELIVERY_METHOD_ALLOWABLE_VALUES}))
+ @RequestParam(defaultValue = "WEB") NotificationDeliveryMethod deliveryMethod,
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
// no permissions
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
- return notificationService.findNotificationsByRecipientIdAndReadStatus(user.getTenantId(), user.getId(), unreadOnly, pageLink);
+ return notificationService.findNotificationsByRecipientIdAndReadStatus(user.getTenantId(), deliveryMethod, user.getId(), unreadOnly, pageLink);
+ }
+
+ @ApiOperation(value = "Get unread notifications count (getUnreadNotificationsCount)",
+ notes = "Returns unread notifications count for chosen delivery method." +
+ AVAILABLE_FOR_ANY_AUTHORIZED_USER)
+ @GetMapping("/notifications/unread/count")
+ @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
+ public Integer getUnreadNotificationsCount(@Parameter(description = "Delivery method", schema = @Schema(allowableValues = {DELIVERY_METHOD_ALLOWABLE_VALUES}))
+ @RequestParam(defaultValue = "MOBILE_APP") NotificationDeliveryMethod deliveryMethod,
+ @AuthenticationPrincipal SecurityUser user) {
+ return notificationService.countUnreadNotificationsByRecipientId(user.getTenantId(), deliveryMethod, user.getId());
}
@ApiOperation(value = "Mark notification as read (markNotificationAsRead)",
@@ -195,9 +211,11 @@ public class NotificationController extends BaseController {
AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@PutMapping("/notifications/read")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
- public void markAllNotificationsAsRead(@AuthenticationPrincipal SecurityUser user) {
+ public void markAllNotificationsAsRead(@Parameter(description = "Delivery method", schema = @Schema(allowableValues = {DELIVERY_METHOD_ALLOWABLE_VALUES}))
+ @RequestParam(defaultValue = "WEB") NotificationDeliveryMethod deliveryMethod,
+ @AuthenticationPrincipal SecurityUser user) {
// no permissions
- notificationCenter.markAllNotificationsAsRead(user.getTenantId(), user.getId());
+ notificationCenter.markAllNotificationsAsRead(user.getTenantId(), deliveryMethod, user.getId());
}
@ApiOperation(value = "Delete notification (deleteNotification)",
diff --git a/application/src/main/java/org/thingsboard/server/controller/QueueController.java b/application/src/main/java/org/thingsboard/server/controller/QueueController.java
index 089ba9bcca..6b90f8f8fd 100644
--- a/application/src/main/java/org/thingsboard/server/controller/QueueController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/QueueController.java
@@ -67,7 +67,7 @@ public class QueueController extends BaseController {
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(value = "/queues", params = {"serviceType", "pageSize", "page"}, method = RequestMethod.GET)
@ResponseBody
- public PageData getTenantQueuesByServiceType(@Parameter(description = QUEUE_SERVICE_TYPE_DESCRIPTION, schema = @Schema(allowableValues = {"TB-RULE-ENGINE", "TB-CORE", "TB-TRANSPORT", "JS-EXECUTOR"}, required = true))
+ public PageData getTenantQueuesByServiceType(@Parameter(description = QUEUE_SERVICE_TYPE_DESCRIPTION, schema = @Schema(allowableValues = {"TB-RULE-ENGINE", "TB-CORE", "TB-TRANSPORT", "JS-EXECUTOR"}, requiredMode = Schema.RequiredMode.REQUIRED))
@RequestParam String serviceType,
@Parameter(description = PAGE_SIZE_DESCRIPTION, required = true)
@RequestParam int pageSize,
@@ -126,7 +126,7 @@ public class QueueController extends BaseController {
@ResponseBody
public Queue saveQueue(@Parameter(description = "A JSON value representing the queue.")
@RequestBody Queue queue,
- @Parameter(description = QUEUE_SERVICE_TYPE_DESCRIPTION, schema = @Schema(allowableValues = {"TB-RULE-ENGINE", "TB-CORE", "TB-TRANSPORT", "JS-EXECUTOR"}, required = true))
+ @Parameter(description = QUEUE_SERVICE_TYPE_DESCRIPTION, schema = @Schema(allowableValues = {"TB-RULE-ENGINE", "TB-CORE", "TB-TRANSPORT", "JS-EXECUTOR"}, requiredMode = Schema.RequiredMode.REQUIRED))
@RequestParam String serviceType) throws ThingsboardException {
checkParameter("serviceType", serviceType);
queue.setTenantId(getCurrentUser().getTenantId());
diff --git a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
index 8d51daab8b..680633e103 100644
--- a/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
@@ -47,8 +47,8 @@ import org.thingsboard.server.actors.tenant.DebugTbRateLimits;
import org.thingsboard.server.common.data.EventInfo;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.edge.Edge;
-import org.thingsboard.server.common.data.event.EventType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
+import org.thingsboard.server.common.data.id.AssetProfileId;
import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.RuleNodeId;
@@ -80,6 +80,7 @@ import org.thingsboard.server.service.security.permission.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
@@ -340,18 +341,8 @@ public class RuleChainController extends BaseController {
RuleNodeId ruleNodeId = new RuleNodeId(toUUID(strRuleNodeId));
checkRuleNode(ruleNodeId, Operation.READ);
TenantId tenantId = getCurrentUser().getTenantId();
- List events = eventService.findLatestEvents(tenantId, ruleNodeId, EventType.DEBUG_RULE_NODE, 2);
- JsonNode result = null;
- if (events != null) {
- for (EventInfo event : events) {
- JsonNode body = event.getBody();
- if (body.has("type") && body.get("type").asText().equals("IN")) {
- result = body;
- break;
- }
- }
- }
- return result;
+ return Optional.ofNullable(eventService.findLatestDebugRuleNodeInEvent(tenantId, ruleNodeId))
+ .map(EventInfo::getBody).orElse(null);
}
@ApiOperation(value = "Is TBEL script executor enabled",
diff --git a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java
index 1908459200..e646c34e4f 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TbResourceController.java
@@ -268,9 +268,9 @@ public class TbResourceController extends BaseController {
"You can specify parameters to filter the results. " + LWM2M_OBJECT_DESCRIPTION + TENANT_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
@GetMapping(value = "/resource/lwm2m")
- public List getLwm2mListObjects(@Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}, required = true))
+ public List getLwm2mListObjects(@Parameter(description = SORT_ORDER_DESCRIPTION, schema = @Schema(allowableValues = {"ASC", "DESC"}, requiredMode = Schema.RequiredMode.REQUIRED))
@RequestParam String sortOrder,
- @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"id", "name"}, required = true))
+ @Parameter(description = SORT_PROPERTY_DESCRIPTION, schema = @Schema(allowableValues = {"id", "name"}, requiredMode = Schema.RequiredMode.REQUIRED))
@RequestParam String sortProperty,
@Parameter(description = "LwM2M Object ids.", required = true)
@RequestParam(required = false) String[] objectIds) throws ThingsboardException {
@@ -315,4 +315,4 @@ public class TbResourceController extends BaseController {
.body(resource);
}
-}
\ No newline at end of file
+}
diff --git a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
index c73ac09cf1..cea449af31 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TelemetryController.java
@@ -25,7 +25,6 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonParser;
import io.swagger.v3.oas.annotations.Parameter;
-import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
@@ -36,7 +35,6 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.PathVariable;
@@ -49,9 +47,6 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
-import org.thingsboard.server.common.data.kv.AggregationParams;
-import org.thingsboard.server.common.data.kv.IntervalType;
-import org.thingsboard.server.common.msg.rule.engine.DeviceAttributesEventNotificationMsg;
import org.thingsboard.server.common.adaptor.JsonConverter;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.EntityType;
@@ -65,6 +60,7 @@ import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.kv.Aggregation;
+import org.thingsboard.server.common.data.kv.AggregationParams;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseDeleteTsKvQuery;
@@ -74,6 +70,7 @@ import org.thingsboard.server.common.data.kv.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.data.kv.DeleteTsKvQuery;
import org.thingsboard.server.common.data.kv.DoubleDataEntry;
+import org.thingsboard.server.common.data.kv.IntervalType;
import org.thingsboard.server.common.data.kv.JsonDataEntry;
import org.thingsboard.server.common.data.kv.KvEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry;
@@ -239,7 +236,7 @@ public class TelemetryController extends BaseController {
public DeferredResult getAttributesByScope(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
- @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, required = true)) @PathVariable("scope") AttributeScope scope,
+ @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, requiredMode = Schema.RequiredMode.REQUIRED)) @PathVariable("scope") AttributeScope scope,
@Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION) @RequestParam(name = "keys", required = false) String keysStr) throws ThingsboardException {
SecurityUser user = getCurrentUser();
return accessValidator.validateEntityAndCallback(getCurrentUser(), Operation.READ_ATTRIBUTES, entityType, entityIdStr,
@@ -356,7 +353,7 @@ public class TelemetryController extends BaseController {
@ResponseBody
public DeferredResult saveDeviceAttributes(
@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable("deviceId") String deviceIdStr,
- @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"}, required = true)) @PathVariable("scope") AttributeScope scope,
+ @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"}, requiredMode = Schema.RequiredMode.REQUIRED)) @PathVariable("scope") AttributeScope scope,
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr);
return saveAttributes(getTenantId(), entityId, scope, request);
@@ -379,7 +376,7 @@ public class TelemetryController extends BaseController {
public DeferredResult saveEntityAttributesV1(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
- @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"})) @PathVariable("scope")AttributeScope scope,
+ @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"})) @PathVariable("scope") AttributeScope scope,
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveAttributes(getTenantId(), entityId, scope, request);
@@ -402,7 +399,7 @@ public class TelemetryController extends BaseController {
public DeferredResult saveEntityAttributesV2(
@Parameter(description = ENTITY_TYPE_PARAM_DESCRIPTION, required = true, schema = @Schema(defaultValue = "DEVICE")) @PathVariable("entityType") String entityType,
@Parameter(description = ENTITY_ID_PARAM_DESCRIPTION, required = true) @PathVariable("entityId") String entityIdStr,
- @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"}, required = true)) @PathVariable("scope")AttributeScope scope,
+ @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE"}, requiredMode = Schema.RequiredMode.REQUIRED)) @PathVariable("scope")AttributeScope scope,
@io.swagger.v3.oas.annotations.parameters.RequestBody(description = ATTRIBUTES_JSON_REQUEST_DESCRIPTION, required = true) @RequestBody JsonNode request) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndId(entityType, entityIdStr);
return saveAttributes(getTenantId(), entityId, scope, request);
@@ -553,7 +550,7 @@ public class TelemetryController extends BaseController {
@ResponseBody
public DeferredResult deleteDeviceAttributes(
@Parameter(description = DEVICE_ID_PARAM_DESCRIPTION, required = true) @PathVariable(DEVICE_ID) String deviceIdStr,
- @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, required = true)) @PathVariable("scope")AttributeScope scope,
+ @Parameter(description = ATTRIBUTES_SCOPE_DESCRIPTION, schema = @Schema(allowableValues = {"SERVER_SCOPE", "SHARED_SCOPE", "CLIENT_SCOPE"}, requiredMode = Schema.RequiredMode.REQUIRED)) @PathVariable("scope")AttributeScope scope,
@Parameter(description = ATTRIBUTES_KEYS_DESCRIPTION, required = true)@RequestParam(name = "keys")String keysStr) throws ThingsboardException {
EntityId entityId = EntityIdFactory.getByTypeAndUuid(EntityType.DEVICE, deviceIdStr);
return deleteAttributes(entityId, scope, keysStr);
@@ -918,4 +915,5 @@ public class TelemetryController extends BaseController {
}
return entry.getValue();
}
+
}
diff --git a/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java b/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java
index 7468e683ab..01487b20a0 100644
--- a/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java
+++ b/application/src/main/java/org/thingsboard/server/controller/TwoFactorAuthConfigController.java
@@ -100,7 +100,7 @@ public class TwoFactorAuthConfigController extends BaseController {
ControllerConstants.AVAILABLE_FOR_ANY_AUTHORIZED_USER)
@PostMapping("/account/config/generate")
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
- public TwoFaAccountConfig generateTwoFaAccountConfig(@Parameter(description = "2FA provider type to generate new account config for", schema = @Schema(defaultValue = "TOTP", required = true))
+ public TwoFaAccountConfig generateTwoFaAccountConfig(@Parameter(description = "2FA provider type to generate new account config for", schema = @Schema(defaultValue = "TOTP", requiredMode = Schema.RequiredMode.REQUIRED))
@RequestParam TwoFaProviderType providerType) throws Exception {
SecurityUser user = getCurrentUser();
return twoFactorAuthService.generateNewAccountConfig(user, providerType);
diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
index f999d31088..eb9d908bab 100644
--- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
+++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
@@ -128,8 +128,11 @@ public class ThingsboardInstallService {
databaseEntitiesUpgradeService.upgradeDatabase("3.6.2");
systemDataLoaderService.updateDefaultNotificationConfigs();
case "3.6.3":
- log.info("Upgrading ThingsBoard from version 3.6.3 to 3.7.0 ...");
+ log.info("Upgrading ThingsBoard from version 3.6.3 to 3.6.4 ...");
databaseEntitiesUpgradeService.upgradeDatabase("3.6.3");
+ case "3.6.4":
+ log.info("Upgrading ThingsBoard from version 3.6.4 to 3.7.0 ...");
+ databaseEntitiesUpgradeService.upgradeDatabase("3.6.4");
//TODO DON'T FORGET to update switch statement in the CacheCleanupService if you need to clear the cache
break;
default:
diff --git a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java
index db051ca0da..ab6d32fb44 100644
--- a/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java
+++ b/application/src/main/java/org/thingsboard/server/service/action/EntityActionService.java
@@ -22,6 +22,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.cluster.TbClusterService;
+import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.HasName;
@@ -127,20 +128,20 @@ public class EntityActionService {
} else {
entityNode = JacksonUtil.newObjectNode();
if (actionType == ActionType.ATTRIBUTES_UPDATED) {
- String scope = extractParameter(String.class, 0, additionalInfo);
+ AttributeScope scope = extractParameter(AttributeScope.class, 0, additionalInfo);
@SuppressWarnings("unchecked")
List attributes = extractParameter(List.class, 1, additionalInfo);
- metaData.putValue(DataConstants.SCOPE, scope);
+ metaData.putValue(DataConstants.SCOPE, scope.name());
if (attributes != null) {
for (AttributeKvEntry attr : attributes) {
JacksonUtil.addKvEntry(entityNode, attr);
}
}
} else if (actionType == ActionType.ATTRIBUTES_DELETED) {
- String scope = extractParameter(String.class, 0, additionalInfo);
+ AttributeScope scope = extractParameter(AttributeScope.class, 0, additionalInfo);
@SuppressWarnings("unchecked")
List keys = extractParameter(List.class, 1, additionalInfo);
- metaData.putValue(DataConstants.SCOPE, scope);
+ metaData.putValue(DataConstants.SCOPE, scope.name());
ArrayNode attrsArrayNode = entityNode.putArray("attributes");
if (keys != null) {
keys.forEach(attrsArrayNode::add);
@@ -249,7 +250,7 @@ public class EntityActionService {
return result;
}
- private void addTimeseries(ObjectNode entityNode, List timeseries) throws Exception {
+ private void addTimeseries(ObjectNode entityNode, List timeseries) {
if (timeseries != null && !timeseries.isEmpty()) {
ArrayNode result = entityNode.putArray("timeseries");
Map> groupedTelemetry = timeseries.stream()
diff --git a/application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java b/application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java
index 974bc06a40..a77b7d0874 100644
--- a/application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java
+++ b/application/src/main/java/org/thingsboard/server/service/device/ClaimDevicesServiceImpl.java
@@ -20,6 +20,7 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.SettableFuture;
+import jakarta.annotation.Nullable;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@@ -28,7 +29,6 @@ import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.RuleEngineTelemetryService;
-import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
@@ -51,9 +51,7 @@ import org.thingsboard.server.dao.device.claim.ReclaimResult;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.queue.util.TbCoreComponent;
-import jakarta.annotation.Nullable;
import java.util.Arrays;
-import java.util.Collections;
import java.util.List;
import java.util.Optional;
@@ -89,16 +87,16 @@ public class ClaimDevicesServiceImpl implements ClaimDevicesService {
Device device = deviceService.findDeviceById(tenantId, deviceId);
Cache cache = cacheManager.getCache(CLAIM_DEVICES_CACHE);
List