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 576b1432ff..83a96c6fe6 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 @@ -5,204 +5,99 @@ "mobileOrder": null, "configuration": { "widgets": { - "81987f19-3eac-e4ce-b790-d96e9b54d9a0": { + "5eb79712-5c24-3060-7e4f-6af36b8f842d": { "type": "timeseries", - "sizeX": 12, - "sizeY": 7, + "sizeX": 24, + "sizeY": 5, "config": { "datasources": [ { "type": "entity", - "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018", "dataKeys": [ { - "name": "successfulMsgs", + "name": "ruleEngineException", "type": "timeseries", - "label": "${entityName} Successful", - "color": "#4caf50", + "label": "Rule Chain", + "color": "#2196f3", "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "return JSON.parse(value).ruleChainName;" }, - "_hash": 0.15490750967648736 + "_hash": 0.9954481282345906 }, { - "name": "failedMsgs", + "name": "ruleEngineException", "type": "timeseries", - "label": "${entityName} Permanent Failures", - "color": "#ef5350", + "label": "Rule Node", + "color": "#4caf50", "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "return JSON.parse(value).ruleNodeName;" }, - "_hash": 0.4186621166514697 + "_hash": 0.18580357036589978 }, { - "name": "tmpFailed", + "name": "ruleEngineException", "type": "timeseries", - "label": "${entityName} Processing Failures", - "color": "#ffc107", + "label": "Latest Error", + "color": "#f44336", "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "return JSON.parse(value).message;" }, - "_hash": 0.49891007198715376 + "_hash": 0.7255162989552142 } ], - "latestDataKeys": [ - { - "name": "queueName", - "type": "entityField", - "label": "Queue name", - "color": "#ffc107", - "settings": {}, - "_hash": 0.019706324241253403 - }, - { - "name": "serviceId", - "type": "entityField", - "label": "Service Id", - "color": "#607d8b", - "settings": {}, - "_hash": 0.6439850190675356 - } - ] + "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018" } ], "timewindow": { "realtime": { "interval": 1000, - "timewindowMs": 300000 + "timewindowMs": 86400000 }, "aggregation": { "type": "NONE", - "limit": 8640 - }, - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false + "limit": 200 + } }, "showTitle": true, - "backgroundColor": "#fff", + "backgroundColor": "rgb(255, 255, 255)", "color": "rgba(0, 0, 0, 0.87)", "padding": "8px", "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "showMin": true, - "showMax": true, - "showAvg": false, - "showTotal": true - } + "showTimestamp": true, + "displayPagination": true, + "defaultPageSize": 10 }, - "title": "Queue Stats", + "title": "Exceptions", "dropShadow": true, "enableFullscreen": true, "titleStyle": { "fontSize": "16px", "fontWeight": 400 }, - "mobileHeight": null, + "useDashboardTimewindow": false, + "showLegend": false, + "widgetStyle": {}, + "actions": {}, "showTitleIcon": false, "titleIcon": null, "iconColor": "rgba(0, 0, 0, 0.87)", "iconSize": "24px", "titleTooltip": "", - "widgetStyle": {}, - "useDashboardTimewindow": false, - "displayTimewindow": true, - "actions": {} + "displayTimewindow": true }, - "id": "81987f19-3eac-e4ce-b790-d96e9b54d9a0", - "typeFullFqn": "system.charts.basic_timeseries" + "id": "5eb79712-5c24-3060-7e4f-6af36b8f842d", + "typeFullFqn": "system.cards.timeseries_table" }, - "5eb79712-5c24-3060-7e4f-6af36b8f842d": { + "42face47-730d-f930-fef5-2a1ef6304b16": { + "typeFullFqn": "system.time_series_chart", "type": "timeseries", - "sizeX": 24, + "sizeX": 8, "sizeY": 5, "config": { "datasources": [ @@ -211,59 +106,144 @@ "entityAliasId": "140f23dd-e3a0-ed98-6189-03c49d2d8018", "dataKeys": [ { - "name": "ruleEngineException", - "type": "timeseries", - "label": "Rule Chain", - "color": "#2196f3", - "settings": { - "useCellStyleFunction": false, - "useCellContentFunction": true, - "cellContentFunction": "return JSON.parse(value).ruleChainName;" - }, - "_hash": 0.9954481282345906 - }, - { - "name": "ruleEngineException", + "name": "successfulMsgs", "type": "timeseries", - "label": "Rule Node", + "label": "{i18n:api-usage.successful}", "color": "#4caf50", "settings": { - "useCellStyleFunction": false, - "useCellContentFunction": true, - "cellContentFunction": "return JSON.parse(value).ruleNodeName;" + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": false, + "lineType": "solid", + "lineWidth": 2.5, + "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)", + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } }, - "_hash": 0.18580357036589978 + "_hash": 0.15490750967648736, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null }, { - "name": "ruleEngineException", + "name": "failedMsgs", "type": "timeseries", - "label": "Latest Error", - "color": "#f44336", - "settings": { - "useCellStyleFunction": false, - "useCellContentFunction": true, - "cellContentFunction": "return JSON.parse(value).message;" - }, - "_hash": 0.7255162989552142 - } - ], - "latestDataKeys": [ - { - "name": "queueName", - "type": "entityField", - "label": "Queue name", - "color": "#ffc107", + "label": "{i18n:api-usage.permanent-failures}", + "color": "#ef5350", "settings": { - "show": false, - "order": null, - "useCellStyleFunction": false, - "cellStyleFunction": "", - "useCellContentFunction": false, - "cellContentFunction": "", - "defaultColumnVisibility": "visible", - "columnSelectionToDisplay": "enabled" + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": false, + "lineType": "solid", + "lineWidth": 2.5, + "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)", + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } }, - "_hash": 0.15709042234841886, + "_hash": 0.4186621166514697, "aggregationType": null, "units": null, "decimals": null, @@ -272,21 +252,71 @@ "postFuncBody": null }, { - "name": "serviceId", - "type": "entityField", - "label": "Service Id", - "color": "#607d8b", + "name": "tmpFailed", + "type": "timeseries", + "label": "{i18n:api-usage.processing-failures}", + "color": "#ffc107", "settings": { - "show": false, - "order": null, - "useCellStyleFunction": false, - "cellStyleFunction": "", - "useCellContentFunction": false, - "cellContentFunction": "", - "defaultColumnVisibility": "visible", - "columnSelectionToDisplay": "enabled" + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": false, + "lineType": "solid", + "lineWidth": 2.5, + "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)", + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } }, - "_hash": 0.13037127418736705, + "_hash": 0.49891007198715376, "aggregationType": null, "units": null, "decimals": null, @@ -294,53 +324,249 @@ "usePostProcessing": null, "postFuncBody": null } - ] + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } } ], "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, "realtime": { - "interval": 1000, - "timewindowMs": 86400000 + "realtimeType": 0, + "timewindowMs": 300000, + "quickInterval": "CURRENT_DAY", + "interval": 1000 }, "aggregation": { "type": "NONE", - "limit": 200 + "limit": 8640 } }, "showTitle": true, - "backgroundColor": "rgb(255, 255, 255)", + "backgroundColor": "#FFFFFF", "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", + "padding": "0px", "settings": { - "showTimestamp": true, - "displayPagination": true, - "defaultPageSize": 10 + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "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)", + "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": false, + "relativeWidth": 2, + "absoluteWidth": 1800000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": true, + "showMax": true, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 300, + "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" }, - "title": "Exceptions", + "title": "{i18n:api-usage.queue-stats}", "dropShadow": true, "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": false, - "showLegend": false, - "widgetStyle": {}, + "titleStyle": null, + "configMode": "basic", "actions": {}, "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", + "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": "", - "displayTimewindow": true + "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" }, - "id": "5eb79712-5c24-3060-7e4f-6af36b8f842d", - "typeFullFqn": "system.cards.timeseries_table" + "row": 0, + "col": 0, + "id": "42face47-730d-f930-fef5-2a1ef6304b16" }, - "ad3f1417-87a8-750e-fc67-49a2de1466d4": { + "6a74ab56-cb36-e75e-c094-a51a896da94a": { + "typeFullFqn": "system.time_series_chart", "type": "timeseries", - "sizeX": 12, - "sizeY": 7, + "sizeX": 8, + "sizeY": 5, "config": { "datasources": [ { @@ -350,157 +576,386 @@ { "name": "timeoutMsgs", "type": "timeseries", - "label": "${entityName} Permanent Timeouts", + "label": "{i18n:api-usage.permanent-timeouts}", "color": "#4caf50", "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": false, + "lineType": "solid", + "lineWidth": 2.5, + "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)", + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } } - ], - "comparisonSettings": { - "showValuesForComparison": true } }, - "_hash": 0.565222981550328 + "_hash": 0.565222981550328, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null }, { "name": "tmpTimeout", "type": "timeseries", - "label": "${entityName} Processing Timeouts", + "label": "{i18n:api-usage.processing-timeouts}", "color": "#9c27b0", "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": false, + "lineType": "solid", + "lineWidth": 2.5, + "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)", + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } } - ], - "comparisonSettings": { - "showValuesForComparison": true } }, - "_hash": 0.2679547062508352 + "_hash": 0.2679547062508352, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null } ], - "latestDataKeys": [ - { - "name": "queueName", - "type": "entityField", - "label": "Queue name", - "color": "#f44336", - "settings": {}, - "_hash": 0.009348067096302426 - }, - { - "name": "serviceId", - "type": "entityField", - "label": "Service Id", - "color": "#ffc107", - "settings": {}, - "_hash": 0.4586005983243109 - } - ] + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } } ], "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, "realtime": { - "interval": 1000, - "timewindowMs": 300000 + "realtimeType": 0, + "timewindowMs": 300000, + "quickInterval": "CURRENT_DAY", + "interval": 1000 }, "aggregation": { "type": "NONE", "limit": 8640 - }, - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false + } }, "showTitle": true, - "backgroundColor": "#fff", + "backgroundColor": "#FFFFFF", "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", + "padding": "0px", "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } }, + "thresholds": [], + "dataZoom": true, "stack": false, - "tooltipIndividual": false, - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true + "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)", + "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": false, + "relativeWidth": 2, + "absoluteWidth": 1800000 + }, + "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": "bottom", + "sortDataKeys": false, "showMin": true, "showMax": true, "showAvg": false, - "showTotal": true - } + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 300, + "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" }, - "title": "Processing Failures and Timeouts", + "title": "{i18n:api-usage.processing-failures-and-timeouts}", "dropShadow": true, "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "mobileHeight": null, + "titleStyle": null, + "configMode": "basic", + "actions": {}, "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "widgetStyle": {}, + "titleIcon": "thermostat", + "iconColor": "#1F6BDD", "useDashboardTimewindow": false, "displayTimewindow": true, - "actions": {} + "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" }, - "id": "ad3f1417-87a8-750e-fc67-49a2de1466d4", - "typeFullFqn": "system.charts.basic_timeseries" + "row": 0, + "col": 0, + "id": "6a74ab56-cb36-e75e-c094-a51a896da94a" } }, "states": { @@ -510,23 +965,21 @@ "layouts": { "main": { "widgets": { - "81987f19-3eac-e4ce-b790-d96e9b54d9a0": { - "sizeX": 12, - "sizeY": 7, - "mobileHeight": null, - "row": 0, - "col": 0 - }, "5eb79712-5c24-3060-7e4f-6af36b8f842d": { "sizeX": 24, "sizeY": 5, "row": 7, "col": 0 }, - "ad3f1417-87a8-750e-fc67-49a2de1466d4": { + "42face47-730d-f930-fef5-2a1ef6304b16": { + "sizeX": 12, + "sizeY": 7, + "row": 0, + "col": 0 + }, + "6a74ab56-cb36-e75e-c094-a51a896da94a": { "sizeX": 12, "sizeY": 7, - "mobileHeight": null, "row": 0, "col": 12 } @@ -551,9 +1004,12 @@ "id": "140f23dd-e3a0-ed98-6189-03c49d2d8018", "alias": "TbServiceQueues", "filter": { - "type": "entityType", + "type": "assetType", "resolveMultiple": true, - "entityType": "QUEUE_STATS" + "assetNameFilter": "", + "assetTypes": [ + "TbServiceQueue" + ] } } }, @@ -592,6 +1048,5 @@ }, "filters": {} }, - "externalId": null, "name": "Rule Engine Statistics" } \ No newline at end of file diff --git a/application/src/main/data/json/demo/dashboards/thermostats.json b/application/src/main/data/json/demo/dashboards/thermostats.json index 967c1cb253..654a311386 100644 --- a/application/src/main/data/json/demo/dashboards/thermostats.json +++ b/application/src/main/data/json/demo/dashboards/thermostats.json @@ -325,240 +325,6 @@ "id": "7943196b-eedb-d422-f9c3-b32d379ad172", "typeFullFqn": "system.alarm_widgets.alarms_table" }, - "14a19183-f0b2-d6be-0f62-9863f0a51111": { - "type": "timeseries", - "sizeX": 18, - "sizeY": 6, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "temperature", - "type": "timeseries", - "label": "Temperature", - "color": "#ef5350", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": true, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.7852346160709658, - "units": "°C", - "decimals": 1 - } - ], - "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547" - } - ], - "timewindow": { - "realtime": { - "interval": 30000, - "timewindowMs": 3600000 - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "smoothLines": true, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "showMin": true, - "showMax": true, - "showAvg": true, - "showTotal": false - } - }, - "title": "Temperature", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "mobileHeight": null, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "widgetStyle": {}, - "useDashboardTimewindow": false, - "displayTimewindow": true, - "actions": {} - }, - "id": "14a19183-f0b2-d6be-0f62-9863f0a51111", - "typeFullFqn": "system.charts.basic_timeseries" - }, - "07f49fd5-a73b-d74c-c220-362c20af81f4": { - "type": "timeseries", - "sizeX": 18, - "sizeY": 6, - "config": { - "datasources": [ - { - "type": "entity", - "dataKeys": [ - { - "name": "humidity", - "type": "timeseries", - "label": "Humidity", - "color": "#2196f3", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": true, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.28640715926957183, - "units": "%", - "decimals": 0 - } - ], - "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547" - } - ], - "timewindow": { - "realtime": { - "interval": 30000, - "timewindowMs": 3600000 - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "smoothLines": true, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "showMin": true, - "showMax": true, - "showAvg": true, - "showTotal": false - } - }, - "title": "Humidity", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "mobileHeight": null, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "widgetStyle": {}, - "useDashboardTimewindow": false, - "displayTimewindow": true, - "actions": {} - }, - "id": "07f49fd5-a73b-d74c-c220-362c20af81f4", - "typeFullFqn": "system.charts.basic_timeseries" - }, "c4631f94-2db3-523b-4d09-2a1a0a75d93f": { "type": "latest", "sizeX": 6, @@ -668,7 +434,9 @@ "fieldsAlignment": "column", "fieldsInRow": 2, "groupTitle": "${entityName}", - "widgetTitle": "Termostat settings" + "widgetTitle": "Termostat settings", + "columnGap": 10, + "rowGap": 5 }, "title": "New Update Multiple Attributes", "dropShadow": true, @@ -787,8 +555,8 @@ "markerImageSize": 48, "useColorFunction": false, "markerImages": [ - "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiNmNDQzMzZ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+", - "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg==" + "tb-image:dGhlcm1vc3RhdHNfZGFzaGJvYXJkX3dpZGdldF90aGVybW9zdGF0X21hcHNfbWFya2VyX2ltYWdlXzAuc3Zn:IlRoZXJtb3N0YXRzIiBkYXNoYm9hcmQgd2lkZ2V0ICJUaGVybW9zdGF0IG1hcHMiIG1hcmtlciBpbWFnZSAw;data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiNmNDQzMzZ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+", + "tb-image:dGhlcm1vc3RhdHNfZGFzaGJvYXJkX3dpZGdldF90aGVybW9zdGF0X21hcHNfbWFya2VyX2ltYWdlXzEuc3Zn:IlRoZXJtb3N0YXRzIiBkYXNoYm9hcmQgd2lkZ2V0ICJUaGVybW9zdGF0IG1hcHMiIG1hcmtlciBpbWFnZSAx;data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg==" ], "useMarkerImageFunction": true, "colorFunction": "\n", @@ -942,8 +710,8 @@ "markerImageSize": 34, "useColorFunction": false, "markerImages": [ - "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiNmNDQzMzZ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+", - "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg==" + "tb-image:dGhlcm1vc3RhdHNfZGFzaGJvYXJkX3dpZGdldF90aGVybW9zdGF0X21hcHNfbWFya2VyX2ltYWdlXzAuc3Zn:IlRoZXJtb3N0YXRzIiBkYXNoYm9hcmQgd2lkZ2V0ICJUaGVybW9zdGF0IG1hcHMiIG1hcmtlciBpbWFnZSAw;data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiNmNDQzMzZ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+", + "tb-image:dGhlcm1vc3RhdHNfZGFzaGJvYXJkX3dpZGdldF90aGVybW9zdGF0X21hcHNfbWFya2VyX2ltYWdlXzEuc3Zn:IlRoZXJtb3N0YXRzIiBkYXNoYm9hcmQgd2lkZ2V0ICJUaGVybW9zdGF0IG1hcHMiIG1hcmtlciBpbWFnZSAx;data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg==" ], "useMarkerImageFunction": true, "color": "#fe7569", @@ -1093,8 +861,8 @@ "markerImageSize": 34, "useColorFunction": false, "markerImages": [ - "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiNmNDQzMzZ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+", - "data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg==" + "tb-image:dGhlcm1vc3RhdHNfZGFzaGJvYXJkX3dpZGdldF90aGVybW9zdGF0X21hcHNfbWFya2VyX2ltYWdlXzAuc3Zn:IlRoZXJtb3N0YXRzIiBkYXNoYm9hcmQgd2lkZ2V0ICJUaGVybW9zdGF0IG1hcHMiIG1hcmtlciBpbWFnZSAw;data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiNmNDQzMzZ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+", + "tb-image:dGhlcm1vc3RhdHNfZGFzaGJvYXJkX3dpZGdldF90aGVybW9zdGF0X21hcHNfbWFya2VyX2ltYWdlXzEuc3Zn:IlRoZXJtb3N0YXRzIiBkYXNoYm9hcmQgd2lkZ2V0ICJUaGVybW9zdGF0IG1hcHMiIG1hcmtlciBpbWFnZSAx;data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJzdmc0NDA4IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjAiIHk9IjAiIHZpZXdCb3g9IjAgMCAxNTAgMTUwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGU+LnN0MntmaWxsOiMyNzg2MjJ9PC9zdHlsZT48ZyBpZD0ibGF5ZXIxIj48ZyBpZD0icGF0aDY4ODEtMy01LTUtMS04LTQtNC03LTgiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0xNDYuNDM4IC0yNzYuMDI4KSIgb3BhY2l0eT0iLjg5MiI+PHJhZGlhbEdyYWRpZW50IGlkPSJTVkdJRF8xXyIgY3g9IjMwODUuMjE1IiBjeT0iMzE3OC40NTgiIHI9IjQ5LjkwMSIgZ3JhZGllbnRUcmFuc2Zvcm09Im1hdHJpeCguNjc5MyAuMDA3NiAtLjUwOSAuNTYxMiAtMjMyLjYyOSAtMTQxMS43MjUpIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLW9wYWNpdHk9Ii4xODgiLz48L3JhZGlhbEdyYWRpZW50PjxwYXRoIGQ9Ik0yODUuNiAzODguNWMxMC4zLTEyLjQgNC40LTIyLjQtMTQuNC0yMi40LTE4LjkgMC00Mi40IDEwLTUzLjkgMjIuNC0xNi44IDE4IC40IDIzLjUtLjIgMzUtLjEgMS44IDMuOSAxLjggNyAwIDE5LjgtMTEuNSA0Ni41LTE3IDYxLjUtMzUiIGZpbGw9InVybCgjU1ZHSURfMV8pIi8+PC9nPjxwYXRoIGlkPSJwYXRoNjg4MS0zLTUtNS0xLTgtNC00IiBjbGFzcz0ic3QyIiBkPSJNMTI0LjcgNjkuMWMtLjktMjcuNS0yMi4zLTQ5LjgtNDkuOC00OS44cy00OSAyMi4zLTQ5LjggNDkuOGMtMS4zIDQwLjEgMzAuNyA1Mi4yIDQ0LjcgNzggMi4yIDQgOCA0IDEwLjEgMCAxNC4xLTI1LjggNDYuMS0zNy45IDQ0LjgtNzgiLz48L2c+PGcgaWQ9Imc0OTI4Ij48Y2lyY2xlIGlkPSJwYXRoNDk3OCIgY2xhc3M9InN0MiIgY3g9Ijc0LjkiIGN5PSI2OS4xIiByPSI0OS45Ii8+PGcgaWQ9Imc0OTE1Ij48cGF0aCBpZD0icGF0aDY4ODMtMi0zLTUtMi00LTktNC05IiBkPSJNNzQuOCAxMDYuNGMtMjAuNiAwLTM3LjQtMTYuNy0zNy40LTM3LjQgMC0yMC42IDE2LjctMzcuNCAzNy40LTM3LjQgMjAuNiAwIDM3LjQgMTYuNyAzNy40IDM3LjRzLTE2LjcgMzcuNC0zNy40IDM3LjQiIGZpbGw9IiNmZmYiLz48L2c+PC9nPjxwYXRoIGNsYXNzPSJzdDIiIGQ9Ik05NS45IDQ2LjZWNDloLTEwdi0yLjVsMTAgLjF6bS0yIDUuM2gtOHYyLjVoOHYtMi41em0tOCA3LjloNnYtMi41aC02djIuNXptNCAyLjloLTR2Mi41aDR2LTIuNXptLTQgNy44aDJWNjhoLTJ2Mi41em0xLjUgMTRjMCA2LjktNS41IDEyLjUtMTIuMyAxMi41cy0xMi4zLTUuNi0xMi4zLTEyLjVjMC00LjUgMi4zLTguNSA2LjEtMTAuN1Y0NS41YzAtMy41IDIuOC02LjMgNi4yLTYuM3M2LjIgMi44IDYuMiA2LjN2MjguM2MzLjggMi4yIDYuMSA2LjMgNi4xIDEwLjd6bS0yLjQgMGMwLTMuOC0yLjEtNy4yLTUuNC04LjlsLS43LS4zVjQ1LjVjMC0yLjEtMS43LTMuOC0zLjgtMy44LTIuMSAwLTMuOCAxLjctMy44IDMuOHYyOS44bC0uNy4zYy0zLjMgMS43LTUuNCA1LjEtNS40IDguOSAwIDUuNSA0LjQgMTAgOS45IDEwUzg1IDkwIDg1IDg0LjV6bS0yLjEgMGMwIDQuNC0zLjUgOC03LjggOHMtNy44LTMuNi03LjgtOGMwLTMuNiAyLjQtNi44IDUuOC03LjdsLjUtLjFWNjEuNWgzLjF2MTUuMmwuNS4xYzMuMyAxIDUuNyA0LjEgNS43IDcuN3ptLTcuNC01LjNjLS4yLS44LTEtMS40LTEuOS0xLjItMyAuNy01IDMuMy01IDYuNCAwIC45LjcgMS42IDEuNiAxLjZzMS42LS43IDEuNi0xLjZjMC0xLjYgMS4xLTMgMi42LTMuMy43LS4yIDEuMy0xIDEuMS0xLjl6Ii8+PC9zdmc+Cg==" ], "useMarkerImageFunction": true, "color": "#fe7569", @@ -1151,6 +919,786 @@ }, "id": "0a430429-9078-9ae6-2b67-e4a15a2bf8bf", "typeFullFqn": "system.input_widgets.markers_placement_openstreetmap" + }, + "eda8a397-0959-690c-405c-11e2c9b2bc7e": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": "", + "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547", + "dataKeys": [ + { + "name": "temperature", + "type": "timeseries", + "label": "Temperature", + "color": "#EF5350", + "settings": { + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": true, + "lineType": "solid", + "lineWidth": 2.5, + "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)", + "pointShape": "circle", + "pointSize": 14, + "fillAreaSettings": { + "type": "gradient", + "opacity": 0.4, + "gradient": { + "start": 60, + "end": 10 + } + } + }, + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.5973804076994531, + "units": "°C", + "decimals": 1, + "aggregationType": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + }, + "latestDataKeys": [ + { + "name": "temperatureAlarmThreshold", + "type": "attribute", + "label": "temperatureAlarmThreshold", + "color": "#4caf50", + "settings": { + "__thresholdKey": true + }, + "_hash": 0.7120450032526351 + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "timewindowMs": 3600000, + "quickInterval": "CURRENT_DAY", + "interval": 30000 + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + }, + "timezone": null + }, + "showTitle": true, + "backgroundColor": "rgba(0, 0, 0, 0)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": true, + "showMax": true, + "showAvg": true, + "showTotal": false, + "showLatest": false + }, + "thresholds": [ + { + "type": "latestKey", + "yAxisId": "default", + "units": "°C", + "decimals": 0, + "lineColor": "rgb(233, 30, 99)", + "lineType": "solid", + "lineWidth": 2, + "startSymbol": "none", + "startSymbolSize": 5, + "endSymbol": "arrow", + "endSymbolSize": 8, + "showLabel": true, + "labelPosition": "end", + "labelFont": { + "size": 14, + "family": "Roboto", + "weight": "500", + "style": "normal" + }, + "labelColor": "rgb(233, 30, 99)", + "latestKey": "temperatureAlarmThreshold", + "latestKeyType": "attribute" + } + ], + "dataZoom": true, + "stack": false, + "yAxis": { + "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)", + "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)" + }, + "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)", + "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)" + }, + "legendLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "showTooltip": true, + "tooltipTrigger": "axis", + "tooltipValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "tooltipValueColor": "rgba(0, 0, 0, 0.76)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "MMM dd yyyy HH:mm", + "lastUpdateAgo": false, + "custom": false + }, + "tooltipDateFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipDateColor": "rgba(0, 0, 0, 0.76)", + "tooltipDateInterval": false, + "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", + "tooltipBackgroundBlur": 4, + "background": { + "type": "color", + "color": "#fff", + "overlay": { + "enabled": false, + "color": "rgba(255,255,255,0.72)", + "blur": 3 + } + }, + "yAxes": { + "default": { + "units": "°C", + "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": null, + "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, + "min": 0 + } + }, + "noAggregationBarWidthSettings": { + "strategy": "group", + "groupWidth": { + "relative": true, + "relativeWidth": 2, + "absoluteWidth": 1000 + }, + "barWidth": { + "relative": true, + "relativeWidth": 2, + "absoluteWidth": 1000 + } + }, + "animation": { + "animation": true, + "animationThreshold": 2000, + "animationDuration": 500, + "animationEasing": "cubicOut", + "animationDelay": 0, + "animationDurationUpdate": 300, + "animationEasingUpdate": "cubicOut", + "animationDelayUpdate": 0 + }, + "padding": "12px" + }, + "title": "Temperature", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": 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" + }, + "row": 0, + "col": 0, + "id": "eda8a397-0959-690c-405c-11e2c9b2bc7e" + }, + "ac90f089-197f-b767-82c3-2668844265a2": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": "", + "entityAliasId": "12ae98c7-1ea2-52cf-64d5-763e9d993547", + "dataKeys": [ + { + "name": "humidity", + "type": "timeseries", + "label": "Humidity", + "color": "#2196F3", + "settings": { + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": true, + "lineType": "solid", + "lineWidth": 2.5, + "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)", + "pointShape": "circle", + "pointSize": 14, + "fillAreaSettings": { + "type": "gradient", + "opacity": 0.4, + "gradient": { + "start": 60, + "end": 10 + } + } + }, + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.4481337125330429, + "units": "%", + "decimals": 0, + "aggregationType": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + }, + "latestDataKeys": [ + { + "name": "humidityAlarmThreshold", + "type": "attribute", + "label": "humidityAlarmThreshold", + "color": "#4caf50", + "settings": { + "__thresholdKey": true + }, + "_hash": 0.134733085341747 + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "timewindowMs": 3600000, + "quickInterval": "CURRENT_DAY", + "interval": 30000 + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + }, + "timezone": null + }, + "showTitle": true, + "backgroundColor": "rgba(0, 0, 0, 0)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "showLegend": true, + "legendConfig": { + "direction": "column", + "position": "bottom", + "sortDataKeys": false, + "showMin": true, + "showMax": true, + "showAvg": true, + "showTotal": false, + "showLatest": false + }, + "thresholds": [ + { + "type": "latestKey", + "yAxisId": "default", + "units": "%", + "decimals": 0, + "lineColor": "rgb(4, 138, 211)", + "lineType": "solid", + "lineWidth": 2, + "startSymbol": "none", + "startSymbolSize": 5, + "endSymbol": "arrow", + "endSymbolSize": 8, + "showLabel": true, + "labelPosition": "end", + "labelFont": { + "size": 14, + "family": "Roboto", + "weight": "500", + "style": "normal" + }, + "labelColor": "rgb(4, 138, 211)", + "latestKey": "humidityAlarmThreshold", + "latestKeyType": "attribute" + } + ], + "dataZoom": true, + "stack": false, + "yAxis": { + "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)", + "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)" + }, + "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)", + "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)" + }, + "legendLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "showTooltip": true, + "tooltipTrigger": "axis", + "tooltipValueFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "500", + "lineHeight": "16px" + }, + "tooltipValueColor": "rgba(0, 0, 0, 0.76)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "MMM dd yyyy HH:mm", + "lastUpdateAgo": false, + "custom": false + }, + "tooltipDateFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipDateColor": "rgba(0, 0, 0, 0.76)", + "tooltipDateInterval": false, + "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", + "tooltipBackgroundBlur": 4, + "background": { + "type": "color", + "color": "#fff", + "overlay": { + "enabled": false, + "color": "rgba(255,255,255,0.72)", + "blur": 3 + } + }, + "yAxes": { + "default": { + "units": "°C", + "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": null, + "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, + "min": 0 + } + }, + "noAggregationBarWidthSettings": { + "strategy": "group", + "groupWidth": { + "relative": true, + "relativeWidth": 2, + "absoluteWidth": 1000 + }, + "barWidth": { + "relative": true, + "relativeWidth": 2, + "absoluteWidth": 1000 + } + }, + "animation": { + "animation": true, + "animationThreshold": 2000, + "animationDuration": 500, + "animationEasing": "cubicOut", + "animationDelay": 0, + "animationDurationUpdate": 300, + "animationEasingUpdate": "cubicOut", + "animationDelayUpdate": 0 + }, + "padding": "12px" + }, + "title": "Humidity", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": 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" + }, + "row": 0, + "col": 0, + "id": "ac90f089-197f-b767-82c3-2668844265a2" } }, "states": { @@ -1226,20 +1774,6 @@ "layouts": { "main": { "widgets": { - "14a19183-f0b2-d6be-0f62-9863f0a51111": { - "sizeX": 18, - "sizeY": 6, - "mobileHeight": null, - "row": 0, - "col": 6 - }, - "07f49fd5-a73b-d74c-c220-362c20af81f4": { - "sizeX": 18, - "sizeY": 6, - "mobileHeight": null, - "row": 6, - "col": 6 - }, "c4631f94-2db3-523b-4d09-2a1a0a75d93f": { "sizeX": 6, "sizeY": 6, @@ -1251,6 +1785,18 @@ "sizeY": 6, "row": 6, "col": 0 + }, + "eda8a397-0959-690c-405c-11e2c9b2bc7e": { + "sizeX": 18, + "sizeY": 6, + "row": 0, + "col": 6 + }, + "ac90f089-197f-b767-82c3-2668844265a2": { + "sizeX": 18, + "sizeY": 6, + "row": 6, + "col": 6 } }, "gridSettings": { @@ -1327,6 +1873,5 @@ }, "filters": {} }, - "externalId": null, "name": "Thermostats" } \ No newline at end of file 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 07929028ff..979b241e67 100644 --- a/application/src/main/data/json/system/widget_bundles/charts.json +++ b/application/src/main/data/json/system/widget_bundles/charts.json @@ -2,12 +2,16 @@ "widgetsBundle": { "alias": "charts", "title": "Charts", - "image": "tb-image:Y2hhcnRzX3N5c3RlbV9idW5kbGVfaW1hZ2UucG5n:IkNoYXJ0cyIgc3lzdGVtIGJ1bmRsZSBpbWFnZQ==;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAAC91BMVEX////K5vz/wQchlvP0QzZgfYtMr1C20ujl5eUAAADi4uL/ySX9/v7/9+Dg4OA8o/TK2eT+6Ob1Wk7q9f7x8fHw8PHy8/L5+vn09PTT2t/p6ekznvRfs/b+wxHLy8v8/Pz/6KL///3+8/JRsVX6+/vq9uqTw+rn5+diuWVNq/bk5OTY2NjNzc2+vr6lzqfqop3u1Yr/wgzD4/w2oPSs2Pv4+Pnj4+S83/v8xBj/wgr0+v5itfdGqPX/1lv29vZUrvXHx8fH5fx2vvgunPTO5OvQ4+Oryd35xSG23fuRy/qAw/n39/jt7e3U4NK3t7eqqqprufdasfY/pPXL5PHu7u7r6+vf3t3/8srW3sjCwsK6urqxsbGw2vum1fuGxvn//fj/+urtzVf3/P/i8v6g0vvL5vqa0PqMyfns7Ozm5ubl1IL/333p0nLtz1/zyj/7/f/S6v17wfjM5fR4uevS0tJtiJX/3HLq0Wrxy0b1yTj/zDD/xhn9/v/u9/7/+PgpmvT//PPS4dj8w77/663c2qnm03yXzvpwu/dmt/fv7+/m6+2uz+ra4eX93tzV1dX/7LOurq6tra3g15j/4of/2WfvzFH1V0v/0kj0STz3yDH5xyjZ7f2gyerR4t7/9tzb3Nv/9dT61tPY3b/Z3Lrb27KMoavf2J92j5v/5JPrz2P2ZFn/xx++4fvr7/Dg6Ou+ztD8zcquvcT/8MKarbakpKSCmqT5n5iTz5Zmgo/5kIn3cWfpx0z1T0P/zz7U6/3P4+bO3ObO19vF0tq80dm6x82jtLz/7rni1YyJy4zc7/3O6Pz+7uz94d/Y7tm437qx3LPIz7P6sauo1ar/5pv6opuRkZHi1o97xH7aynr4gHdpvGzhymZbtl7zxC36wRC30ui30uTM1Njz69TM6M3F5cbC5MPDz7/p3bt/q52huI64wIf4gXjfzG67umTVvUDk8+Tm5N3a185yss2TwMjCz8T7ta/7s610qaWOr5PSy5H4hn33fnX3eG83NCs7AAAOvklEQVR42tzaSYyLYRzH8Z9SaV6P19a+7+u1VKmUWhJLK7baDpY2StKZCSNmwsFMHJDgYM84WGIZIUgkloMQYt8FsUessUWIiy0ODk4ccDDzvG2fvn3eeVvK+758LxKHJ/30eZ63/+kM/p+ICGVhHA5r9Gj0m7wQv1LlHiRrt8BZSae7kiSS+IWk55WhLXAWRIY4uW/fLaiHTFBsZdO9k5OOgRApJAiCCqm+78LTNZVQwoIQTcgoIlJfL/abPBpEEPYIf6kwiioeEqMpGbToQpLqGknjasWoj6D4BNhYUBVDMpqK+MSw8i9A4kLB1+kvExPE4ZBYOORHEUmiShwMkbmXZ0ZZ51QIUcUg8lPWb4hh2TAYlBDjjoTEREnvOnrs1JlFbre7Izo0b96p95qJQ5ZDlz+sOhCSEAhYwWunGgw0Ckk3YmazZfrzJTsM4hdSYB29pSkYhDVo0q5csCg5CiKLEWTbsN/NYhDW8LvIRjwJB0EiYpDtRobBQ1hTryCbqv5VyKpufKthnCISpIvfYgADCGssu/gpz9+EtHbxtYJhceboeMBtDmF1moBMXo8jIHLWQU5wCAbhGzsruyeqAyDBcj+0lP1uMwhf79lIV1ZjO4RkPwmOHHCbQ/gGZe+8R7Ib4lGgtX6RuxCEb9yY7PsRtBfiVZmjMIRvZbPsTbMVIovcuTKF8I2bz94SGyGZAxE84y4Owjcoc+PDEfsgUhm0TrmLhfD1ngWaX7QNQjKfIDfdxUP4xkKrxmcXJKSkL/r5kiDNh0BLJPZAiJj+94y7NEin5eyk2gFRFdCOuUuA0BY0uSVXO/ONLR1iuCGxRSVA0s0HzZuA1rTInul0+bbN+dr8YUiZBNoJd+mQztASQSt/LiWRtAoighZfVAIkW7Pc0xqsnC7Vo94iSCTENqR0yPDM9yp0Qyqfd7VuR2plNEYWlQJhzdZd900Nd6QcRBBuGM0Cwq/00QAyXhDC+SfrmvvPQOaAts4HliU7Ele5b35KgXSeARqJWg1RY0hHNtw6UBJk5fDBy9lGWw0RQVtVrQ0pJ8zHeFPFMO7qWQkRQOvmWtKNs/CQQgqWlLASwh6+I+n/P9UsR26e+QXIuClMwSJRayFeH2itXFqHD7bmLGaQTmOHzAMtRKBLtBaiRrQr4mIt3do6oFn2F4CMGNxhHpt0FOgSfhsS6G5QIYhA0FiFi8Ysu6lFub7/fFOQUUP0ByqmQleU/C6kXQu+9gUg7K7n12XuXmqJpS16yKgJwwpcCqgxOyBbXQYNWKxZ4m9Onc+FrB04CwD87849MPnZKSXZAVnsMm7otopA+hdX5zXIlGZUEXx37nXLlg9NID6vHZBtrqbbVlGlWdZj2Hx6ueX3dxoUDZ0zgUgpOyAjXaaNPFmFTPKHOz2oogBEqXEgpKEdt1c1Kh6+oIp0d0wgcdWZEJerNQDygikK7UjCaXck3UGgbgXkHrmQJ2Z3ZJ0dkLmFj1YAG3deAB7kQj6YQNZJdkAOFnJ0WYUVh1q0qAP650B6mkASiqUQEbTNhSAVwIWG9S61A3mVdbxCTsFa6ArJlkI8fjS2t4BjLlBHF7wYgJSFPOI/N1gCLIXUKNrA2cXUsSSAdpe0FZ8B79OOHhJy8sjQJVoJYQ/Jy6aQavgvZpY8DtwxeGZBhK0Qf5gbtvg2A2ezS27vDj8dUHrptkD2QJccshLC3rjbZgMKsK8F6yXwufFgPTCZ2oGEZDEkrN32qqYvyeEqdG+fu+hX4BGdfM1OlkCA2OnToX7TR1sD8fkKDSm7ge+6RXduBHnxiPstJA+TUn02JZG0AsIuyWaT0eRxC32HVsAPfSIB+K/GlUqyBfVWQNhLqFpqMprkL3sBeUkq9IUiAOTTHimpbgHxeq8bQbzGbTSAbPe+NYJ4vRIypbzcuMWNJlx10EXKidHDN9a3r6D0jYP4fIYQn3H7jCA+Q4jP50UmIoBW3cVkNNGljSq5qRL0KWVgWXS0EPU3Pcov1kYTvosBsGJh5BUO2gBRVG5LuNGE7xn/9wasoAAbIBBJU7ekGoH0aMK3jzkiyKs2bgOEfQasasWPJs+MDJlRRSss8X88DDsgbEtOmowmfC9B8/iQXyhmE0QJQWsxP5qY9AUAiXqRnyzAJgiiMmhVl/WjySdjARtVEFXAJcjFQnr24HtVCiQoQmv3UpPRhMVGFQIuSUXRkJZ8vUqBoMwHrYrMM/hyAPe50cR0VGEPYxshEGVorR6qfRNfDdz7VMCxvQ58Qgx2QvwiyUgGuBraisaOHzJhXDq7AnypGtgKgVKLrITOvLRA3cWmduPsPRgUEWAzBGoC6SoOL61Gto1n2/PPq5d1VTAqWE5sh6B2HdK1Pgld9x9/O7QzuxU/zh5fAeP85TLsh/xk545xGgaCAIr+Eo1GUzn2apsVkoUlF0Bjl1wASsgZnKOk4AZIKDfIPaDiBtyEDhdZkLPVRPKrd4uv3WKqYaz52/XH6+F4PHzmG+apy0OIWqDMfN9FCNpXlEv2gJMQ6CZKNdbgJ4S4U4rU1uIphGANBWKv+Aoh2abgW1XgLQSmMx9FozR4DKEdO2WxYDX4DIFgUcnJn/QbArXFtHTBgOsQCCKV8o+hs0nBfQjos437RI6GznYDcBEhQNr01k+hZaaP+87sZWAxFfm+OfUm77envmR7lyHb+wyRnuVSeLoy+TXGamC1Wq1WPxoFtAfR4rpyDpE6ugxDHZSIy4EBw1AHfaAYcQDHCCemLMcQEULij3pkUAiNeoR8odq04eERE2ljz2HgkZOBNrHmxkY09wgHBtCmppBZsXMxOzu7mvMBqhuvTc8Y6Q4K9GAEgULNfOI15u8zKU6vISpGtHUdSmhfIQq3G6cxQkGgF1Eaa43S2m3qg6yLNWWsiPGIhLa2ro4OjT1iIh3QzQgDRkEmBDQKm3ta8ztrdnilGDEysjsG2CQT4REdHm1dGsfIbHtNE0Z2RjgwkZ6NW2NyoWY9v0xhmwdUMUijl/Ep4jM7J608YhUnHQd2DwKkB1nh0lhg7BRrhFAJ1dgmHTPgxe8afhlzqHsQIMAaV5Eg3YasDq7RxDhuYD0i3M6fBncPAjhqtmHVaOIci6QKWaO5ZkDSAHkE0N7ZhTQVhnF879NFRcSCPoiOZxREMLKLbdHO2sbcV/uAlXOQm87VdHMwp+kMt5lR4lZCYmBJaFqhIklR0K2UQZDUVRBEEH1c9UVFX1DdtffMebZ5ds5ZlJT0v5j4+Pzf9/zeZ+/zvuzCdZ3f2SY1GuSIBQRZCT2LMVQ1goqAIHmZ7ujig6zx+nTpzkl3HVYQ1KhYW2g8dFKqZLIWGg1V59lnrP8jIA3WkNOoKLMHrrI9D6M23aF8426nU464QJCNkOPELfm9xKsm+n4XyO61Da16pbfuREhNEM62xuILy0iv8+WN1aA5qUeI21inCIhEq2+SQ9ozPZe24FpYK41Om5ynIq1r+UBslU6pRmxcdowQa3ROdaUh2/4FgKCr4hERo1sKuwCjtcrecwQyknX63377HMKF5wZpIKR93CCNGltj3U7rKZQrbhB3YpxZX+M9UVZywiZoBVzvAbSrXz3sOGeZwxnyh5Mf8RA1+z0Vkh0Lb7994vYy3S6ui+cpYs9yLPzKiDP0lQKYvp4NtYv75oYaMW7jMDJ6+RSefb+9YvvzdkLcvuPHu+kMzqfMN0tI0lpQkXqdAek1Po6KXHs9rUpMCFhFJmS2QIIEMuyaC/VWzr1FxV5OY1YDJAw5fAq7QmO3ZkvknkxN0Q8lkeCKFIJ09arRjOtUVV0RkAM92meA1TImHMQ0BC1RczOAJZkJyTUB3HYNGiUSAGLyA8SiCDW25Z02XHvk4N237+MWUD198noNC8iWGx1HMMMj84Al/eOBQJBoCwyZ8D5pSZscdMhKINFutU6OBIC4h4FKMiFukOPVPU3aThnQolT4dfjs5ku5IFuqO64AwIc3ZrqyYQpg0CEEJDgIFtqDgpMUqGImnBVQtEp79UzWneRUbDzKMlYwrIKWUSQIpOPcKsjI8uHdwNgMcj0Ix+k30BHt5eoDdNKlCM7pPLNHKs8WPEUCNHfzg8SAmgc2RWRADbzAx/UyO8rKMRun144cHDAXjGWOA6SCSBiICvdmbdPmahexc94Q/PJGe4Ue/VzT3TOdALCqqVrkFStzVjFCgmpqlAfkMZBuxMgRB3g6hpA+gGhFxyPDuJvGH4UTkFYi5Q4yY01QYBlHSCDIw9OZfxpzngjkGtSVXRc2n8UIuDbNN9I5rTQpM6w5JgNZZIYLZABUE/mRiWEAfwbfPOknAYCaSt7J/bU5acpsrgjA4AwSDJK9eYtteQa9LkQ3gNNNN7WnD6ygM04ULn93MwCZMhUFSapgsjAWfUy3YjdTAmzMK5AqPutY3p0A2SxCpYLsltoLDMrcJpz21Et9LMM6/ABU2MU+0xgJj1m2DW7FMmZTFBi7Z+P4jxYKht2oZJBDZZULDF5CngPS1dvLPiyNkjKxzOSgIMbeWN0JGI4lXcUe0TURswD4Tah0EINTv9AQUNxjQHxSfbFhx1pwVUyFM41awB9kB0HBbp52F3wwgVDpICGNnM0QqjqaBdkpVnIMOzYIQEbMuSF8oMejLI8oOCQMpEJS0T//cVCdWMluMOjqMyDPCS/3sG6M8sTMBFz0gf7nQTb09+8ol2AQfJ8dKXbx1JWtwLpNbOO96r6cBpA9yd5xX0yD5TpL1u8MbccgtfvnP6BrJeqKksulNtzSNO1ClszhV4Fs7rSfAqr7ty4//x5pUDRyGJRir6hLfVLgTI7mOZQIUG60yCAaH6fBS9y36/SCZ8IoqqkUyMbRYoPYeAwBY9XVUmYajeHzbBItOgivwWYtcabRCPkI/YUgvxAyoSUCgv6D/AdZEiB79y4REI9niYAsmYrQWvkPS5SvlSJW/SNZS1F4q/CrZoeApNr15fv5szySDQKmlJSXe0oD8QjKr60VkLR9fW0Nf9bGw+US/qxNnq2SP1KRGgFJF9d5BKTt6z+8jz+rQlJzmCflJ6e3vqMkTdRjAAAAAElFTkSuQmCC", + "image": "tb-image:Y2hhcnRzLnN2Zw==:IkNoYXJ0cyIgc3lzdGVtIGJ1bmRsZSBpbWFnZQ==;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF80MTk4XzExMDQ5MykiPgo8cGF0aCBkPSJNMTkuMjQ2MSAyMC45OTY5TDQuOTU0NDggNTQuMzI1NEwwIDYzLjVWNzZIOTZWNjAuOTU2MVY0NS4zMzMzTDg4Ljg5ODcgNjkuMDQ2MUw4NC45NDA3IDU0LjIxNTNMODMuMjIzMyA1OC41NzczTDc1Ljg4MjQgNzQuOTM3NkM3MC45ODg5IDU3LjE0MzMgNjYuOTExMSA1MC41MDM2IDYxLjU4NTEgMzMuMDIwOUw1Ni4wODk3IDU5LjEzMDlINDkuMDIyMUw0Ny42NTcgMTYuNzAyTDQ0LjkwNzYgMEwzMS44MjUgNDEuNjcwOEwyOC43MTQxIDQwLjM2MjdMMTkuMjQ2MSAyMC45OTY5WiIgZmlsbD0idXJsKCNwYWludDBfbGluZWFyXzQxOThfMTEwNDkzKSIvPgo8cGF0aCBkPSJNMCA2My42NDg3TDUuMDAxMDkgNTMuNDg4TDE5LjQyNzEgMjAuMzk4OEwyOC45ODQyIDQwLjYyNTVMMzIuMTI0NCA0MS45MjQzTDQ0Ljg2OTYgMUw0Ny43MzkxIDE2LjE3MDNDNDguMjc3MiAzMi42MjA5IDQ4LjQxODQgNDIuNTQ5NCA0OC45NTY1IDU5SDU2TDYxLjU2NTIgMzIuMTIzM0w3NS45MTMgNzRMODUuMDE4NSA1My40NDEzTDg4Ljk1NjUgNjguMDE3Nkw5NiA0NC43MTk0IiBzdHJva2U9InVybCgjcGFpbnQxX2xpbmVhcl80MTk4XzExMDQ5MykiIHN0cm9rZS13aWR0aD0iMC43NzY1ODIiLz4KPC9nPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDFfNDE5OF8xMTA0OTMpIj4KPHBhdGggZD0iTTAgMEw5NiAxLjYwODcyZS0wNSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC4yNTQxNyIvPgo8cGF0aCBkPSJNMCAxOUw5NiAxOSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC4yNTQxNyIvPgo8cGF0aCBkPSJNMCAzOEw5NiAzOCIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC4yNTQxNyIvPgo8cGF0aCBkPSJNMCA1N0w5NiA1NyIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC4yNTQxNyIvPgo8cGF0aCBkPSJNMCA3Nkw5NiA3NiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC4yNTQxNyIvPgo8L2c+CjxyZWN0IHg9IjEwNCIgd2lkdGg9Ijk2IiBoZWlnaHQ9Ijc2IiByeD0iMS4xMzQzMyIgZmlsbD0id2hpdGUiLz4KPG1hc2sgaWQ9InBhdGgtOS1vdXRzaWRlLTFfNDE5OF8xMTA0OTMiIG1hc2tVbml0cz0idXNlclNwYWNlT25Vc2UiIHg9IjE1Mi4wMTgiIHk9IjMuMjUiIHdpZHRoPSIzNSIgaGVpZ2h0PSI1MSIgZmlsbD0iYmxhY2siPgo8cmVjdCBmaWxsPSJ3aGl0ZSIgeD0iMTUyLjAxOCIgeT0iMy4yNSIgd2lkdGg9IjM1IiBoZWlnaHQ9IjUxIi8+CjxwYXRoIGQ9Ik0xNTMuMzQxIDcuMjYyNDlDMTUzLjM5IDUuODg2MDYgMTU0LjU0NiA0Ljc5OTU1IDE1NS45MTUgNC45NTA2MUMxNjAuNjM1IDUuNDcxNDMgMTY1LjE5OCA2Ljk5ODQgMTY5LjI5MyA5LjQzOTU4QzE3NC4xMDQgMTIuMzA3MyAxNzguMTEzIDE2LjM0MzggMTgwLjk0NyAyMS4xNzQ0QzE4My43ODEgMjYuMDA1IDE4NS4zNDkgMzEuNDczMiAxODUuNTA1IDM3LjA3MTZDMTg1LjYzOCA0MS44MzczIDE4NC43NDUgNDYuNTY1OSAxODIuODk3IDUwLjkzOThDMTgyLjM2MSA1Mi4yMDg1IDE4MC44NDggNTIuNjg4MiAxNzkuNjIzIDUyLjA1ODZDMTc4LjM5OCA1MS40MjkxIDE3Ny45MjcgNDkuOTI5IDE3OC40NDYgNDguNjUzM0MxNzkuOTE5IDQ1LjAzMjUgMTgwLjYyOSA0MS4xMzY1IDE4MC41MiAzNy4yMTA5QzE4MC4zODcgMzIuNDUyMiAxNzkuMDU0IDI3LjgwNDIgMTc2LjY0NSAyMy42OTgyQzE3NC4yMzYgMTkuNTkyMiAxNzAuODI5IDE2LjE2MTIgMTY2Ljc0IDEzLjcyMzZDMTYzLjM2NiAxMS43MTI4IDE1OS42MTkgMTAuNDMxNyAxNTUuNzQgOS45NTE1MkMxNTQuMzczIDkuNzgyMzQgMTUzLjI5MyA4LjYzODkxIDE1My4zNDEgNy4yNjI0OVoiLz4KPC9tYXNrPgo8cGF0aCBkPSJNMTUzLjM0MSA3LjI2MjQ5QzE1My4zOSA1Ljg4NjA2IDE1NC41NDYgNC43OTk1NSAxNTUuOTE1IDQuOTUwNjFDMTYwLjYzNSA1LjQ3MTQzIDE2NS4xOTggNi45OTg0IDE2OS4yOTMgOS40Mzk1OEMxNzQuMTA0IDEyLjMwNzMgMTc4LjExMyAxNi4zNDM4IDE4MC45NDcgMjEuMTc0NEMxODMuNzgxIDI2LjAwNSAxODUuMzQ5IDMxLjQ3MzIgMTg1LjUwNSAzNy4wNzE2QzE4NS42MzggNDEuODM3MyAxODQuNzQ1IDQ2LjU2NTkgMTgyLjg5NyA1MC45Mzk4QzE4Mi4zNjEgNTIuMjA4NSAxODAuODQ4IDUyLjY4ODIgMTc5LjYyMyA1Mi4wNTg2QzE3OC4zOTggNTEuNDI5MSAxNzcuOTI3IDQ5LjkyOSAxNzguNDQ2IDQ4LjY1MzNDMTc5LjkxOSA0NS4wMzI1IDE4MC42MjkgNDEuMTM2NSAxODAuNTIgMzcuMjEwOUMxODAuMzg3IDMyLjQ1MjIgMTc5LjA1NCAyNy44MDQyIDE3Ni42NDUgMjMuNjk4MkMxNzQuMjM2IDE5LjU5MjIgMTcwLjgyOSAxNi4xNjEyIDE2Ni43NCAxMy43MjM2QzE2My4zNjYgMTEuNzEyOCAxNTkuNjE5IDEwLjQzMTcgMTU1Ljc0IDkuOTUxNTJDMTU0LjM3MyA5Ljc4MjM0IDE1My4yOTMgOC42Mzg5MSAxNTMuMzQxIDcuMjYyNDlaIiBmaWxsPSIjRkY0RDVBIi8+CjxwYXRoIGQ9Ik0xNTMuMzQxIDcuMjYyNDlDMTUzLjM5IDUuODg2MDYgMTU0LjU0NiA0Ljc5OTU1IDE1NS45MTUgNC45NTA2MUMxNjAuNjM1IDUuNDcxNDMgMTY1LjE5OCA2Ljk5ODQgMTY5LjI5MyA5LjQzOTU4QzE3NC4xMDQgMTIuMzA3MyAxNzguMTEzIDE2LjM0MzggMTgwLjk0NyAyMS4xNzQ0QzE4My43ODEgMjYuMDA1IDE4NS4zNDkgMzEuNDczMiAxODUuNTA1IDM3LjA3MTZDMTg1LjYzOCA0MS44MzczIDE4NC43NDUgNDYuNTY1OSAxODIuODk3IDUwLjkzOThDMTgyLjM2MSA1Mi4yMDg1IDE4MC44NDggNTIuNjg4MiAxNzkuNjIzIDUyLjA1ODZDMTc4LjM5OCA1MS40MjkxIDE3Ny45MjcgNDkuOTI5IDE3OC40NDYgNDguNjUzM0MxNzkuOTE5IDQ1LjAzMjUgMTgwLjYyOSA0MS4xMzY1IDE4MC41MiAzNy4yMTA5QzE4MC4zODcgMzIuNDUyMiAxNzkuMDU0IDI3LjgwNDIgMTc2LjY0NSAyMy42OTgyQzE3NC4yMzYgMTkuNTkyMiAxNzAuODI5IDE2LjE2MTIgMTY2Ljc0IDEzLjcyMzZDMTYzLjM2NiAxMS43MTI4IDE1OS42MTkgMTAuNDMxNyAxNTUuNzQgOS45NTE1MkMxNTQuMzczIDkuNzgyMzQgMTUzLjI5MyA4LjYzODkxIDE1My4zNDEgNy4yNjI0OVoiIGZpbGw9IndoaXRlIiBmaWxsLW9wYWNpdHk9IjAuMSIvPgo8cGF0aCBkPSJNMTUzLjM0MSA3LjI2MjQ5QzE1My4zOSA1Ljg4NjA2IDE1NC41NDYgNC43OTk1NSAxNTUuOTE1IDQuOTUwNjFDMTYwLjYzNSA1LjQ3MTQzIDE2NS4xOTggNi45OTg0IDE2OS4yOTMgOS40Mzk1OEMxNzQuMTA0IDEyLjMwNzMgMTc4LjExMyAxNi4zNDM4IDE4MC45NDcgMjEuMTc0NEMxODMuNzgxIDI2LjAwNSAxODUuMzQ5IDMxLjQ3MzIgMTg1LjUwNSAzNy4wNzE2QzE4NS42MzggNDEuODM3MyAxODQuNzQ1IDQ2LjU2NTkgMTgyLjg5NyA1MC45Mzk4QzE4Mi4zNjEgNTIuMjA4NSAxODAuODQ4IDUyLjY4ODIgMTc5LjYyMyA1Mi4wNTg2QzE3OC4zOTggNTEuNDI5MSAxNzcuOTI3IDQ5LjkyOSAxNzguNDQ2IDQ4LjY1MzNDMTc5LjkxOSA0NS4wMzI1IDE4MC42MjkgNDEuMTM2NSAxODAuNTIgMzcuMjEwOUMxODAuMzg3IDMyLjQ1MjIgMTc5LjA1NCAyNy44MDQyIDE3Ni42NDUgMjMuNjk4MkMxNzQuMjM2IDE5LjU5MjIgMTcwLjgyOSAxNi4xNjEyIDE2Ni43NCAxMy43MjM2QzE2My4zNjYgMTEuNzEyOCAxNTkuNjE5IDEwLjQzMTcgMTU1Ljc0IDkuOTUxNTJDMTU0LjM3MyA5Ljc4MjM0IDE1My4yOTMgOC42Mzg5MSAxNTMuMzQxIDcuMjYyNDlaIiBzdHJva2U9IndoaXRlIiBzdHJva2Utd2lkdGg9IjEuOTMzNzEiIG1hc2s9InVybCgjcGF0aC05LW91dHNpZGUtMV80MTk4XzExMDQ5MykiLz4KPHBhdGggZD0iTTE1MS4xOTUgNy4yNjI0OUMxNTEuMTQ3IDUuODg2MDYgMTQ5Ljk5IDQuNzk5NTUgMTQ4LjYyMSA0Ljk1MDYyQzE0My43MjcgNS40OTA2NyAxMzkuMDAzIDcuMTEyMzcgMTM0Ljc5NiA5LjcxMDUzQzEyOS44ODEgMTIuNzQ2NSAxMjUuODQxIDE3LjAxMDkgMTIzLjA3NSAyMi4wODM1QzEyMC4zMDkgMjcuMTU2MSAxMTguOTEzIDMyLjg2MTggMTE5LjAyNCAzOC42MzgzQzExOS4xMzUgNDQuNDE0OCAxMjAuNzQ5IDUwLjA2MjcgMTIzLjcwOCA1NS4wMjU0QzEyNi42NjYgNTkuOTg4MSAxMzAuODY2IDY0LjA5NDMgMTM1Ljg5NSA2Ni45MzkzQzE0MC45MjQgNjkuNzg0MyAxNDYuNjA3IDcxLjI3IDE1Mi4zODQgNzEuMjQ5OEMxNTguMTYyIDcxLjIyOTYgMTYzLjgzNCA2OS43MDQ0IDE2OC44NDMgNjYuODI0M0MxNzMuMTI5IDY0LjM1OTYgMTc2LjgwMiA2MC45NzU0IDE3OS42MDUgNTYuOTI3M0MxODAuMzg5IDU1Ljc5NDkgMTc5Ljk4NCA1NC4yNjA3IDE3OC43OTYgNTMuNTYzN0MxNzcuNjA4IDUyLjg2NjggMTc2LjA4OSA1My4yNzI0IDE3NS4yOSA1NC4zOTQzQzE3Mi45MzYgNTcuNjk5OSAxNjkuODkxIDYwLjQ2ODEgMTY2LjM1NyA2Mi41MDA3QzE2Mi4wOTkgNjQuOTQ4NyAxNTcuMjc4IDY2LjI0NTIgMTUyLjM2NyA2Ni4yNjIzQzE0Ny40NTYgNjYuMjc5NSAxNDIuNjI1IDY1LjAxNjcgMTM4LjM1MSA2Mi41OTg0QzEzNC4wNzcgNjAuMTgwMiAxMzAuNTA2IDU2LjY4OTkgMTI3Ljk5MiA1Mi40NzE2QzEyNS40NzcgNDguMjUzMyAxMjQuMTA1IDQzLjQ1MjYgMTI0LjAxMSAzOC41NDI2QzEyMy45MTYgMzMuNjMyNSAxMjUuMTAzIDI4Ljc4MjcgMTI3LjQ1NCAyNC40NzFDMTI5LjgwNSAyMC4xNTkzIDEzMy4yMzkgMTYuNTM0NSAxMzcuNDE3IDEzLjk1MzlDMTQwLjg4NiAxMS44MTEzIDE0NC43NjkgMTAuNDUgMTQ4Ljc5NiA5Ljk1MTUxQzE1MC4xNjMgOS43ODIzNCAxNTEuMjQzIDguNjM4OTEgMTUxLjE5NSA3LjI2MjQ5WiIgZmlsbD0iIzRDQUY1MCIvPgo8cGF0aCBkPSJNMTQ3LjEwNiAyOS4yMzQ0VjMzLjVIMTQ2LjU0OVYyOS4yMzQ0SDE0Ny4xMDZaTTE0OC40NzcgMjkuMjM0NFYyOS42OTczSDE0NS4xODFWMjkuMjM0NEgxNDguNDc3Wk0xNDguNTkxIDMxLjk1MDJWMzEuODgyOEMxNDguNTkxIDMxLjY1NDMgMTQ4LjYyNSAzMS40NDI0IDE0OC42OTEgMzEuMjQ3MUMxNDguNzU3IDMxLjA0OTggMTQ4Ljg1MyAzMC44Nzg5IDE0OC45NzggMzAuNzM0NEMxNDkuMTAzIDMwLjU4NzkgMTQ5LjI1NCAzMC40NzQ2IDE0OS40MzIgMzAuMzk0NUMxNDkuNjEgMzAuMzEyNSAxNDkuODA5IDMwLjI3MTUgMTUwLjAzIDMwLjI3MTVDMTUwLjI1MiAzMC4yNzE1IDE1MC40NTMgMzAuMzEyNSAxNTAuNjMgMzAuMzk0NUMxNTAuODEgMzAuNDc0NiAxNTAuOTYyIDMwLjU4NzkgMTUxLjA4NyAzMC43MzQ0QzE1MS4yMTQgMzAuODc4OSAxNTEuMzExIDMxLjA0OTggMTUxLjM3NyAzMS4yNDcxQzE1MS40NDQgMzEuNDQyNCAxNTEuNDc3IDMxLjY1NDMgMTUxLjQ3NyAzMS44ODI4VjMxLjk1MDJDMTUxLjQ3NyAzMi4xNzg3IDE1MS40NDQgMzIuMzkwNiAxNTEuMzc3IDMyLjU4NTlDMTUxLjMxMSAzMi43ODEyIDE1MS4yMTQgMzIuOTUyMSAxNTEuMDg3IDMzLjA5ODZDMTUwLjk2MiAzMy4yNDMyIDE1MC44MTEgMzMuMzU2NCAxNTAuNjMzIDMzLjQzODVDMTUwLjQ1OCAzMy41MTg2IDE1MC4yNTggMzMuNTU4NiAxNTAuMDM2IDMzLjU1ODZDMTQ5LjgxMyAzMy41NTg2IDE0OS42MTMgMzMuNTE4NiAxNDkuNDM1IDMzLjQzODVDMTQ5LjI1NyAzMy4zNTY0IDE0OS4xMDUgMzMuMjQzMiAxNDguOTc4IDMzLjA5ODZDMTQ4Ljg1MyAzMi45NTIxIDE0OC43NTcgMzIuNzgxMiAxNDguNjkxIDMyLjU4NTlDMTQ4LjYyNSAzMi4zOTA2IDE0OC41OTEgMzIuMTc4NyAxNDguNTkxIDMxLjk1MDJaTTE0OS4xMzMgMzEuODgyOFYzMS45NTAyQzE0OS4xMzMgMzIuMTA4NCAxNDkuMTUyIDMyLjI1NzggMTQ5LjE4OSAzMi4zOTg0QzE0OS4yMjYgMzIuNTM3MSAxNDkuMjgyIDMyLjY2MDIgMTQ5LjM1NiAzMi43Njc2QzE0OS40MzIgMzIuODc1IDE0OS41MjcgMzIuOTYgMTQ5LjY0IDMzLjAyMjVDMTQ5Ljc1MyAzMy4wODMgMTQ5Ljg4NSAzMy4xMTMzIDE1MC4wMzYgMzMuMTEzM0MxNTAuMTg0IDMzLjExMzMgMTUwLjMxNCAzMy4wODMgMTUwLjQyNSAzMy4wMjI1QzE1MC41MzkgMzIuOTYgMTUwLjYzMiAzMi44NzUgMTUwLjcwNyAzMi43Njc2QzE1MC43ODEgMzIuNjYwMiAxNTAuODM2IDMyLjUzNzEgMTUwLjg3NCAzMi4zOTg0QzE1MC45MTMgMzIuMjU3OCAxNTAuOTMyIDMyLjEwODQgMTUwLjkzMiAzMS45NTAyVjMxLjg4MjhDMTUwLjkzMiAzMS43MjY2IDE1MC45MTMgMzEuNTc5MSAxNTAuODc0IDMxLjQ0MDRDMTUwLjgzNiAzMS4yOTk4IDE1MC43OCAzMS4xNzU4IDE1MC43MDQgMzEuMDY4NEMxNTAuNjI5IDMwLjk1OSAxNTAuNTM2IDMwLjg3MyAxNTAuNDIyIDMwLjgxMDVDMTUwLjMxMSAzMC43NDggMTUwLjE4IDMwLjcxNjggMTUwLjAzIDMwLjcxNjhDMTQ5Ljg4MSAzMC43MTY4IDE0OS43NSAzMC43NDggMTQ5LjYzNyAzMC44MTA1QzE0OS41MjYgMzAuODczIDE0OS40MzIgMzAuOTU5IDE0OS4zNTYgMzEuMDY4NEMxNDkuMjgyIDMxLjE3NTggMTQ5LjIyNiAzMS4yOTk4IDE0OS4xODkgMzEuNDQwNEMxNDkuMTUyIDMxLjU3OTEgMTQ5LjEzMyAzMS43MjY2IDE0OS4xMzMgMzEuODgyOFpNMTUzLjQ4NCAzMC4zMzAxVjMwLjc0NjFIMTUxLjc3VjMwLjMzMDFIMTUzLjQ4NFpNMTUyLjM1IDI5LjU1OTZIMTUyLjg5MlYzMi43MTQ4QzE1Mi44OTIgMzIuODIyMyAxNTIuOTA5IDMyLjkwMzMgMTUyLjk0MiAzMi45NThDMTUyLjk3NSAzMy4wMTI3IDE1My4wMTggMzMuMDQ4OCAxNTMuMDcxIDMzLjA2NjRDMTUzLjEyNCAzMy4wODQgMTUzLjE4IDMzLjA5MjggMTUzLjI0MSAzMy4wOTI4QzE1My4yODYgMzMuMDkyOCAxNTMuMzMzIDMzLjA4ODkgMTUzLjM4MSAzMy4wODExQzE1My40MzIgMzMuMDcxMyAxNTMuNDcgMzMuMDYzNSAxNTMuNDk2IDMzLjA1NzZMMTUzLjQ5OSAzMy41QzE1My40NTYgMzMuNTEzNyAxNTMuMzk5IDMzLjUyNjQgMTUzLjMyOSAzMy41MzgxQzE1My4yNiAzMy41NTE4IDE1My4xNzcgMzMuNTU4NiAxNTMuMDggMzMuNTU4NkMxNTIuOTQ3IDMzLjU1ODYgMTUyLjgyNSAzMy41MzIyIDE1Mi43MTMgMzMuNDc5NUMxNTIuNjAyIDMzLjQyNjggMTUyLjUxMyAzMy4zMzg5IDE1Mi40NDcgMzMuMjE1OEMxNTIuMzgyIDMzLjA5MDggMTUyLjM1IDMyLjkyMjkgMTUyLjM1IDMyLjcxMTlWMjkuNTU5NlpNMTU1Ljk4OSAzMi45NThWMzEuMzI2MkMxNTUuOTg5IDMxLjIwMTIgMTU1Ljk2MyAzMS4wOTI4IDE1NS45MTMgMzEuMDAxQzE1NS44NjQgMzAuOTA3MiAxNTUuNzkgMzAuODM1IDE1NS42OSAzMC43ODQyQzE1NS41OSAzMC43MzM0IDE1NS40NjcgMzAuNzA4IDE1NS4zMjEgMzAuNzA4QzE1NS4xODQgMzAuNzA4IDE1NS4wNjQgMzAuNzMxNCAxNTQuOTYgMzAuNzc4M0MxNTQuODU5IDMwLjgyNTIgMTU0Ljc3OSAzMC44ODY3IDE1NC43MiAzMC45NjI5QzE1NC42NjQgMzEuMDM5MSAxNTQuNjM1IDMxLjEyMTEgMTU0LjYzNSAzMS4yMDlIMTU0LjA5M0MxNTQuMDkzIDMxLjA5NTcgMTU0LjEyMyAzMC45ODM0IDE1NC4xODEgMzAuODcyMUMxNTQuMjQgMzAuNzYwNyAxNTQuMzI0IDMwLjY2MDIgMTU0LjQzMyAzMC41NzAzQzE1NC41NDQgMzAuNDc4NSAxNTQuNjc3IDMwLjQwNjIgMTU0LjgzMiAzMC4zNTM1QzE1NC45ODggMzAuMjk4OCAxNTUuMTYyIDMwLjI3MTUgMTU1LjM1MyAzMC4yNzE1QzE1NS41ODMgMzAuMjcxNSAxNTUuNzg3IDMwLjMxMDUgMTU1Ljk2MiAzMC4zODg3QzE1Ni4xNCAzMC40NjY4IDE1Ni4yNzkgMzAuNTg1IDE1Ni4zNzggMzAuNzQzMkMxNTYuNDggMzAuODk5NCAxNTYuNTMxIDMxLjA5NTcgMTU2LjUzMSAzMS4zMzJWMzIuODA4NkMxNTYuNTMxIDMyLjkxNDEgMTU2LjU0IDMzLjAyNjQgMTU2LjU1NyAzMy4xNDU1QzE1Ni41NzcgMzMuMjY0NiAxNTYuNjA1IDMzLjM2NzIgMTU2LjY0MiAzMy40NTMxVjMzLjVIMTU2LjA3N0MxNTYuMDQ5IDMzLjQzNzUgMTU2LjAyOCAzMy4zNTQ1IDE1Ni4wMTIgMzMuMjUxQzE1NS45OTcgMzMuMTQ1NSAxNTUuOTg5IDMzLjA0NzkgMTU1Ljk4OSAzMi45NThaTTE1Ni4wODMgMzEuNTc4MUwxNTYuMDg4IDMxLjk1OUgxNTUuNTQxQzE1NS4zODYgMzEuOTU5IDE1NS4yNDkgMzEuOTcxNyAxNTUuMTI3IDMxLjk5NzFDMTU1LjAwNiAzMi4wMjA1IDE1NC45MDUgMzIuMDU2NiAxNTQuODIzIDMyLjEwNTVDMTU0Ljc0MSAzMi4xNTQzIDE1NC42NzggMzIuMjE1OCAxNTQuNjM1IDMyLjI5QzE1NC41OTIgMzIuMzYyMyAxNTQuNTcxIDMyLjQ0NzMgMTU0LjU3MSAzMi41NDQ5QzE1NC41NzEgMzIuNjQ0NSAxNTQuNTkzIDMyLjczNTQgMTU0LjYzOCAzMi44MTc0QzE1NC42ODMgMzIuODk5NCAxNTQuNzUgMzIuOTY0OCAxNTQuODQgMzMuMDEzN0MxNTQuOTMyIDMzLjA2MDUgMTU1LjA0NCAzMy4wODQgMTU1LjE3NyAzMy4wODRDMTU1LjM0MyAzMy4wODQgMTU1LjQ5IDMzLjA0ODggMTU1LjYxNyAzMi45Nzg1QzE1NS43NDQgMzIuOTA4MiAxNTUuODQ0IDMyLjgyMjMgMTU1LjkxOCAzMi43MjA3QzE1NS45OTUgMzIuNjE5MSAxNTYuMDM2IDMyLjUyMDUgMTU2LjA0MiAzMi40MjQ4TDE1Ni4yNzMgMzIuNjg1NUMxNTYuMjU5IDMyLjc2NzYgMTU2LjIyMiAzMi44NTg0IDE1Ni4xNjIgMzIuOTU4QzE1Ni4xMDEgMzMuMDU3NiAxNTYuMDIgMzMuMTUzMyAxNTUuOTE4IDMzLjI0NTFDMTU1LjgxOSAzMy4zMzUgMTU1LjcgMzMuNDEwMiAxNTUuNTYxIDMzLjQ3MDdDMTU1LjQyNCAzMy41MjkzIDE1NS4yNyAzMy41NTg2IDE1NS4wOTggMzMuNTU4NkMxNTQuODgzIDMzLjU1ODYgMTU0LjY5NSAzMy41MTY2IDE1NC41MzMgMzMuNDMyNkMxNTQuMzczIDMzLjM0ODYgMTU0LjI0OCAzMy4yMzYzIDE1NC4xNTggMzMuMDk1N0MxNTQuMDcgMzIuOTUzMSAxNTQuMDI2IDMyLjc5MzkgMTU0LjAyNiAzMi42MTgyQzE1NC4wMjYgMzIuNDQ4MiAxNTQuMDU5IDMyLjI5ODggMTU0LjEyNSAzMi4xNjk5QzE1NC4xOTIgMzIuMDM5MSAxNTQuMjg4IDMxLjkzMDcgMTU0LjQxMyAzMS44NDQ3QzE1NC41MzggMzEuNzU2OCAxNTQuNjg4IDMxLjY5MDQgMTU0Ljg2NCAzMS42NDU1QzE1NS4wNCAzMS42MDA2IDE1NS4yMzYgMzEuNTc4MSAxNTUuNDUzIDMxLjU3ODFIMTU2LjA4M1pNMTU3Ljk3MiAyOVYzMy41SDE1Ny40MjdWMjlIMTU3Ljk3MloiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuMzgiLz4KPHBhdGggZD0iTTE0NS43MzIgMzYuOTM5NVY0NS41SDE0NC4zMlYzOC42MTUyTDE0Mi4yMjggMzkuMzI0MlYzOC4xNTgyTDE0NS41NjIgMzYuOTM5NUgxNDUuNzMyWk0xNTQuMjc1IDQzLjE3MzhDMTU0LjI3NSA0My43MDUxIDE1NC4xNTIgNDQuMTUyMyAxNTMuOTA2IDQ0LjUxNTZDMTUzLjY2IDQ0Ljg3ODkgMTUzLjMyNCA0NS4xNTQzIDE1Mi44OTggNDUuMzQxOEMxNTIuNDc2IDQ1LjUyNTQgMTUyIDQ1LjYxNzIgMTUxLjQ2OCA0NS42MTcyQzE1MC45MzcgNDUuNjE3MiAxNTAuNDU4IDQ1LjUyNTQgMTUwLjAzMyA0NS4zNDE4QzE0OS42MDcgNDUuMTU0MyAxNDkuMjcxIDQ0Ljg3ODkgMTQ5LjAyNSA0NC41MTU2QzE0OC43NzkgNDQuMTUyMyAxNDguNjU2IDQzLjcwNTEgMTQ4LjY1NiA0My4xNzM4QzE0OC42NTYgNDIuODIyMyAxNDguNzI0IDQyLjUwMzkgMTQ4Ljg2MSA0Mi4yMTg4QzE0OC45OTggNDEuOTI5NyAxNDkuMTkxIDQxLjY4MTYgMTQ5LjQ0MSA0MS40NzQ2QzE0OS42OTUgNDEuMjYzNyAxNDkuOTkyIDQxLjEwMTYgMTUwLjMzMiA0MC45ODgzQzE1MC42NzUgNDAuODc1IDE1MS4wNSA0MC44MTg0IDE1MS40NTcgNDAuODE4NEMxNTEuOTk2IDQwLjgxODQgMTUyLjQ3OCA0MC45MTggMTUyLjkwNCA0MS4xMTcyQzE1My4zMyA0MS4zMTY0IDE1My42NjQgNDEuNTkxOCAxNTMuOTA2IDQxLjk0MzRDMTU0LjE1MiA0Mi4yOTQ5IDE1NC4yNzUgNDIuNzA1MSAxNTQuMjc1IDQzLjE3MzhaTTE1Mi44NTcgNDMuMTAzNUMxNTIuODU3IDQyLjgxODQgMTUyLjc5OCA0Mi41Njg0IDE1Mi42ODEgNDIuMzUzNUMxNTIuNTY0IDQyLjEzODcgMTUyLjQgNDEuOTcyNyAxNTIuMTg5IDQxLjg1NTVDMTUxLjk3OCA0MS43MzgzIDE1MS43MzQgNDEuNjc5NyAxNTEuNDU3IDQxLjY3OTdDMTUxLjE3NSA0MS42Nzk3IDE1MC45MzEgNDEuNzM4MyAxNTAuNzI0IDQxLjg1NTVDMTUwLjUxNyA0MS45NzI3IDE1MC4zNTUgNDIuMTM4NyAxNTAuMjM4IDQyLjM1MzVDMTUwLjEyNSA0Mi41Njg0IDE1MC4wNjggNDIuODE4NCAxNTAuMDY4IDQzLjEwMzVDMTUwLjA2OCA0My4zOTI2IDE1MC4xMjUgNDMuNjQyNiAxNTAuMjM4IDQzLjg1MzVDMTUwLjM1MSA0NC4wNjA1IDE1MC41MTMgNDQuMjE4OCAxNTAuNzI0IDQ0LjMyODFDMTUwLjkzNSA0NC40Mzc1IDE1MS4xODMgNDQuNDkyMiAxNTEuNDY4IDQ0LjQ5MjJDMTUxLjc1MyA0NC40OTIyIDE1MiA0NC40Mzc1IDE1Mi4yMDcgNDQuMzI4MUMxNTIuNDE0IDQ0LjIxODggMTUyLjU3NCA0NC4wNjA1IDE1Mi42ODcgNDMuODUzNUMxNTIuOCA0My42NDI2IDE1Mi44NTcgNDMuMzkyNiAxNTIuODU3IDQzLjEwMzVaTTE1NC4wODIgMzkuMjEyOUMxNTQuMDgyIDM5LjYzODcgMTUzLjk2OCA0MC4wMTc2IDE1My43NDIgNDAuMzQ5NkMxNTMuNTE5IDQwLjY4MTYgMTUzLjIxIDQwLjk0MzQgMTUyLjgxNiA0MS4xMzQ4QzE1Mi40MjEgNDEuMzIyMyAxNTEuOTcyIDQxLjQxNiAxNTEuNDY4IDQxLjQxNkMxNTAuOTYgNDEuNDE2IDE1MC41MDcgNDEuMzIyMyAxNTAuMTA5IDQxLjEzNDhDMTQ5LjcxNCA0MC45NDM0IDE0OS40MDQgNDAuNjgxNiAxNDkuMTc3IDQwLjM0OTZDMTQ4Ljk1NSA0MC4wMTc2IDE0OC44NDMgMzkuNjM4NyAxNDguODQzIDM5LjIxMjlDMTQ4Ljg0MyAzOC43MDUxIDE0OC45NTUgMzguMjc3MyAxNDkuMTc3IDM3LjkyOTdDMTQ5LjQwNCAzNy41NzgxIDE0OS43MTQgMzcuMzEwNSAxNTAuMTA5IDM3LjEyN0MxNTAuNTAzIDM2Ljk0MzQgMTUwLjk1NSAzNi44NTE2IDE1MS40NjIgMzYuODUxNkMxNTEuOTcgMzYuODUxNiAxNTIuNDIxIDM2Ljk0MzQgMTUyLjgxNiAzNy4xMjdDMTUzLjIxIDM3LjMxMDUgMTUzLjUxOSAzNy41NzgxIDE1My43NDIgMzcuOTI5N0MxNTMuOTY4IDM4LjI3NzMgMTU0LjA4MiAzOC43MDUxIDE1NC4wODIgMzkuMjEyOVpNMTUyLjY2OSAzOS4yNTk4QzE1Mi42NjkgMzkuMDA1OSAxNTIuNjE5IDM4Ljc4MzIgMTUyLjUxNyAzOC41OTE4QzE1Mi40MTkgMzguMzk2NSAxNTIuMjgxIDM4LjI0NDEgMTUyLjEwMSAzOC4xMzQ4QzE1MS45MjEgMzguMDI1NCAxNTEuNzA4IDM3Ljk3MDcgMTUxLjQ2MiAzNy45NzA3QzE1MS4yMTYgMzcuOTcwNyAxNTEuMDAzIDM4LjAyMzQgMTUwLjgyNCAzOC4xMjg5QzE1MC42NDQgMzguMjM0NCAxNTAuNTA1IDM4LjM4MjggMTUwLjQwOCAzOC41NzQyQzE1MC4zMSAzOC43NjU2IDE1MC4yNjEgMzguOTk0MSAxNTAuMjYxIDM5LjI1OThDMTUwLjI2MSAzOS41MjE1IDE1MC4zMSAzOS43NSAxNTAuNDA4IDM5Ljk0NTNDMTUwLjUwNSA0MC4xMzY3IDE1MC42NDQgNDAuMjg3MSAxNTAuODI0IDQwLjM5NjVDMTUxLjAwNyA0MC41MDU5IDE1MS4yMjIgNDAuNTYwNSAxNTEuNDY4IDQwLjU2MDVDMTUxLjcxNCA0MC41NjA1IDE1MS45MjcgNDAuNTA1OSAxNTIuMTA3IDQwLjM5NjVDMTUyLjI4NyA0MC4yODcxIDE1Mi40MjUgNDAuMTM2NyAxNTIuNTIzIDM5Ljk0NTNDMTUyLjYyMSAzOS43NSAxNTIuNjY5IDM5LjUyMTUgMTUyLjY2OSAzOS4yNTk4Wk0xNTcuMTc1IDQwLjU5NTdIMTU4LjAxOUMxNTguMzQ3IDQwLjU5NTcgMTU4LjYxOSA0MC41MzkxIDE1OC44MzMgNDAuNDI1OEMxNTkuMDUyIDQwLjMxMjUgMTU5LjIxNCA0MC4xNTYyIDE1OS4zMiAzOS45NTdDMTU5LjQyNSAzOS43NTc4IDE1OS40NzggMzkuNTI5MyAxNTkuNDc4IDM5LjI3MTVDMTU5LjQ3OCAzOS4wMDIgMTU5LjQyOSAzOC43NzE1IDE1OS4zMzIgMzguNTgwMUMxNTkuMjM4IDM4LjM4NDggMTU5LjA5MyAzOC4yMzQ0IDE1OC44OTggMzguMTI4OUMxNTguNzA3IDM4LjAyMzQgMTU4LjQ2MiAzNy45NzA3IDE1OC4xNjYgMzcuOTcwN0MxNTcuOTE2IDM3Ljk3MDcgMTU3LjY4OSAzOC4wMjE1IDE1Ny40ODYgMzguMTIzQzE1Ny4yODcgMzguMjIwNyAxNTcuMTI4IDM4LjM2MTMgMTU3LjAxMSAzOC41NDQ5QzE1Ni44OTQgMzguNzI0NiAxNTYuODM1IDM4LjkzOTUgMTU2LjgzNSAzOS4xODk1SDE1NS40MTdDMTU1LjQxNyAzOC43MzYzIDE1NS41MzcgMzguMzM0IDE1NS43NzUgMzcuOTgyNEMxNTYuMDEzIDM3LjYzMDkgMTU2LjMzNyAzNy4zNTU1IDE1Ni43NDggMzcuMTU2MkMxNTcuMTYyIDM2Ljk1MzEgMTU3LjYyNiAzNi44NTE2IDE1OC4xNDIgMzYuODUxNkMxNTguNjkzIDM2Ljg1MTYgMTU5LjE3MyAzNi45NDM0IDE1OS41ODMgMzcuMTI3QzE1OS45OTggMzcuMzA2NiAxNjAuMzIgMzcuNTc2MiAxNjAuNTUgMzcuOTM1NUMxNjAuNzgxIDM4LjI5NDkgMTYwLjg5NiAzOC43NDAyIDE2MC44OTYgMzkuMjcxNUMxNjAuODk2IDM5LjUxMzcgMTYwLjgzOSAzOS43NTk4IDE2MC43MjYgNDAuMDA5OEMxNjAuNjEzIDQwLjI1OTggMTYwLjQ0NSA0MC40ODgzIDE2MC4yMjIgNDAuNjk1M0MxNjAgNDAuODk4NCAxNTkuNzIyIDQxLjA2NDUgMTU5LjM5IDQxLjE5MzRDMTU5LjA1OCA0MS4zMTg0IDE1OC42NzMgNDEuMzgwOSAxNTguMjM2IDQxLjM4MDlIMTU3LjE3NVY0MC41OTU3Wk0xNTcuMTc1IDQxLjY5NzNWNDAuOTIzOEgxNTguMjM2QzE1OC43MzYgNDAuOTIzOCAxNTkuMTYyIDQwLjk4MjQgMTU5LjUxMyA0MS4wOTk2QzE1OS44NjkgNDEuMjE2OCAxNjAuMTU4IDQxLjM3ODkgMTYwLjM4IDQxLjU4NTlDMTYwLjYwMyA0MS43ODkxIDE2MC43NjUgNDIuMDIxNSAxNjAuODY3IDQyLjI4MzJDMTYwLjk3MiA0Mi41NDQ5IDE2MS4wMjUgNDIuODIyMyAxNjEuMDI1IDQzLjExNTJDMTYxLjAyNSA0My41MTM3IDE2MC45NTMgNDMuODY5MSAxNjAuODA4IDQ0LjE4MTZDMTYwLjY2NyA0NC40OTAyIDE2MC40NjYgNDQuNzUyIDE2MC4yMDUgNDQuOTY2OEMxNTkuOTQzIDQ1LjE4MTYgMTU5LjYzNiA0NS4zNDM4IDE1OS4yODUgNDUuNDUzMUMxNTguOTM3IDQ1LjU2MjUgMTU4LjU1OCA0NS42MTcyIDE1OC4xNDggNDUuNjE3MkMxNTcuNzgxIDQ1LjYxNzIgMTU3LjQyOSA0NS41NjY0IDE1Ny4wOTMgNDUuNDY0OEMxNTYuNzU3IDQ1LjM2MzMgMTU2LjQ1NyA0NS4yMTI5IDE1Ni4xOTEgNDUuMDEzN0MxNTUuOTI1IDQ0LjgxMDUgMTU1LjcxNCA0NC41NTg2IDE1NS41NTggNDQuMjU3OEMxNTUuNDA2IDQzLjk1MzEgMTU1LjMzIDQzLjYwMTYgMTU1LjMzIDQzLjIwMzFIMTU2Ljc0MkMxNTYuNzQyIDQzLjQ1NyAxNTYuOCA0My42ODE2IDE1Ni45MTcgNDMuODc3QzE1Ny4wMzkgNDQuMDY4NCAxNTcuMjA3IDQ0LjIxODggMTU3LjQyMSA0NC4zMjgxQzE1Ny42NCA0NC40Mzc1IDE1Ny44OSA0NC40OTIyIDE1OC4xNzEgNDQuNDkyMkMxNTguNDY4IDQ0LjQ5MjIgMTU4LjcyNCA0NC40Mzk1IDE1OC45MzkgNDQuMzM0QzE1OS4xNTQgNDQuMjI4NSAxNTkuMzE4IDQ0LjA3MjMgMTU5LjQzMSA0My44NjUyQzE1OS41NDggNDMuNjU4MiAxNTkuNjA3IDQzLjQwODIgMTU5LjYwNyA0My4xMTUyQzE1OS42MDcgNDIuNzgzMiAxNTkuNTQyIDQyLjUxMzcgMTU5LjQxNCA0Mi4zMDY2QzE1OS4yODUgNDIuMDk5NiAxNTkuMTAxIDQxLjk0NzMgMTU4Ljg2MyA0MS44NDk2QzE1OC42MjUgNDEuNzQ4IDE1OC4zNDMgNDEuNjk3MyAxNTguMDE5IDQxLjY5NzNIMTU3LjE3NVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuODciLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAyXzQxOThfMTEwNDkzKSI+CjxwYXRoIGQ9Ik0yIDg4TDE5NyA4OCIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC41Ii8+CjxwYXRoIGQ9Ik0yIDEwNkwxOTcgMTA2IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIgc3Ryb2tlLXdpZHRoPSIwLjUiLz4KPHBhdGggZD0iTTIgMTI0TDE5NyAxMjQiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjEyIiBzdHJva2Utd2lkdGg9IjAuNSIvPgo8cGF0aCBkPSJNMiAxNDJMMTk3IDE0MiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiIHN0cm9rZS13aWR0aD0iMC41Ii8+CjxwYXRoIGQ9Ik0yIDE2MEwxOTcgMTYwIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMTAzLjY5NSA5MUMxMDMuNjk1IDg5Ljg5NTQgMTA0LjU5IDg5IDEwNS42OTUgODlIMTE3LjY5NUMxMTguNzk5IDg5IDExOS42OTUgODkuODk1NCAxMTkuNjk1IDkxVjE2MEgxMDMuNjk1VjkxWiIgZmlsbD0iIzQxOTZGNiIvPgo8cGF0aCBkPSJNMTM2IDEyNEMxMzYgMTIyLjg5NSAxMzYuODk1IDEyMiAxMzggMTIySDE1MEMxNTEuMTA1IDEyMiAxNTIgMTIyLjg5NSAxNTIgMTI0VjE2MEgxMzZWMTI0WiIgZmlsbD0iIzQxOTZGNiIvPgo8cGF0aCBkPSJNNy42OTQ4MiAxNDNDNy42OTQ4MiAxNDEuODk1IDguNTkwMjUgMTQxIDkuNjk0ODIgMTQxSDIxLjY5NDhDMjIuNzk5NCAxNDEgMjMuNjk0OCAxNDEuODk1IDIzLjY5NDggMTQzVjE2MEg3LjY5NDgyVjE0M1oiIGZpbGw9IiM0MTk2RjYiLz4KPHBhdGggZD0iTTM5LjY5NDggMTA3QzM5LjY5NDggMTA1Ljg5NSA0MC41OTAzIDEwNSA0MS42OTQ4IDEwNUg1My42OTQ4QzU0Ljc5OTQgMTA1IDU1LjY5NDggMTA1Ljg5NSA1NS42OTQ4IDEwN1YxNjBIMzkuNjk0OFYxMDdaIiBmaWxsPSIjNDE5NkY2Ii8+CjxwYXRoIGQ9Ik03MiAxMzJDNzIgMTMwLjg5NSA3Mi44OTU0IDEzMCA3NCAxMzBIODZDODcuMTA0NiAxMzAgODggMTMwLjg5NSA4OCAxMzJWMTYwSDcyVjEzMloiIGZpbGw9IiM0MTk2RjYiLz4KPHBhdGggZD0iTTE2Ny42OTUgMTUwQzE2Ny42OTUgMTQ4Ljg5NSAxNjguNTkgMTQ4IDE2OS42OTUgMTQ4SDE4MS42OTVDMTgyLjc5OSAxNDggMTgzLjY5NSAxNDguODk1IDE4My42OTUgMTUwVjE2MEgxNjcuNjk1VjE1MFoiIGZpbGw9IiM0MTk2RjYiLz4KPHBhdGggZD0iTTQgMTA5TDE4LjUyMTEgMTAzLjAzMUMxOC41OTMzIDEwMy4wMDEgMTguNjY4NyAxMDIuOTggMTguNzQ1NyAxMDIuOTY4TDU5LjU3NDcgOTYuNTM2NUM1OS42NjE1IDk2LjUyMjggNTkuNzQ5OCA5Ni41MjA3IDU5LjgzNzMgOTYuNTMwMUw5OS4wOTMzIDEwMC43NTVMMTM5LjA0NCA5OS43MTMxTDE4MS4xNDIgOTUuODQwMkMxODEuMjEgOTUuODMzOSAxODEuMjc4IDk1LjgyMDYgMTgxLjM0NCA5NS44MDA1TDE5NyA5MSIgc3Ryb2tlPSIjRkZDMTA3IiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8Y2lyY2xlIGN4PSI1OSIgY3k9Ijk3IiByPSIxLjUiIGZpbGw9IndoaXRlIiBzdHJva2U9IiNGRkMxMDciLz4KPGNpcmNsZSBjeD0iMTguNjk0OCIgY3k9IjEwMy4xNjEiIHI9IjEuNSIgZmlsbD0id2hpdGUiIHN0cm9rZT0iI0ZGQzEwNyIvPgo8Y2lyY2xlIGN4PSIzLjY5NDgyIiBjeT0iMTA5LjE2MSIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjRkZDMTA3Ii8+CjxjaXJjbGUgY3g9IjEwMCIgY3k9IjEwMSIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjRkZDMTA3Ii8+CjxjaXJjbGUgY3g9IjE0MyIgY3k9Ijk5IiByPSIxLjUiIGZpbGw9IndoaXRlIiBzdHJva2U9IiNGRkMxMDciLz4KPGNpcmNsZSBjeD0iMTgwLjY5NSIgY3k9Ijk2LjE2MTEiIHI9IjEuNSIgZmlsbD0id2hpdGUiIHN0cm9rZT0iI0ZGQzEwNyIvPgo8Y2lyY2xlIGN4PSIxOTYiIGN5PSI5MSIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjRkZDMTA3Ii8+CjxwYXRoIGQ9Ik00IDEzMkwxOC42MzkzIDExOS44MTRMNjAuMzA1IDEyNy42TDEwMC4yODIgMTE2TDE0MC4yNTggMTE2TDE3Ny45ODIgMTIyLjE4NEwxOTYgMTI3LjYiIHN0cm9rZT0iIzRDQUY1MCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPGNpcmNsZSBjeD0iNTkiIGN5PSIxMjgiIHI9IjEuNSIgZmlsbD0id2hpdGUiIHN0cm9rZT0iIzRDQUY1MCIvPgo8Y2lyY2xlIGN4PSIxOSIgY3k9IjEyMCIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjNENBRjUwIi8+CjxjaXJjbGUgY3g9IjEwMCIgY3k9IjExNiIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjNENBRjUwIi8+CjxjaXJjbGUgY3g9IjE3OC42OTUiIGN5PSIxMjIiIHI9IjEuNSIgZmlsbD0id2hpdGUiIHN0cm9rZT0iIzRDQUY1MCIvPgo8Y2lyY2xlIGN4PSIxOTYiIGN5PSIxMjgiIHI9IjEuNSIgZmlsbD0id2hpdGUiIHN0cm9rZT0iIzRDQUY1MCIvPgo8Y2lyY2xlIGN4PSIzLjY5NDgyIiBjeT0iMTMxLjE2MSIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjNENBRjUwIi8+CjxjaXJjbGUgY3g9IjE0MyIgY3k9IjExNiIgcj0iMS41IiBmaWxsPSJ3aGl0ZSIgc3Ryb2tlPSIjNENBRjUwIi8+CjwvZz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl80MTk4XzExMDQ5MyIgeDE9IjQ1LjY5ODYiIHkxPSI4NC43NDkzIiB4Mj0iNDUuNjk4NiIgeTI9Ii0xOS45Mzg4IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIG9mZnNldD0iMC4xNzMzODkiIHN0b3AtY29sb3I9IiM2REM3NzAiLz4KPHN0b3Agb2Zmc2V0PSIwLjI2Mjk3OSIgc3RvcC1jb2xvcj0iIzZEQzc3MCIvPgo8c3RvcCBvZmZzZXQ9IjAuMjY3OTY5IiBzdG9wLWNvbG9yPSIjRkZDMTA3Ii8+CjxzdG9wIG9mZnNldD0iMC40NDYxNzgiIHN0b3AtY29sb3I9IiNGRkMxMDciLz4KPHN0b3Agb2Zmc2V0PSIwLjQ0ODMxNiIgc3RvcC1jb2xvcj0iI0ZEOEYzQyIvPgo8c3RvcCBvZmZzZXQ9IjAuNjI0Mzg3IiBzdG9wLWNvbG9yPSIjRkQ4RjNDIi8+CjxzdG9wIG9mZnNldD0iMC42MzA4MDMiIHN0b3AtY29sb3I9IiNGMjczN0MiLz4KPC9saW5lYXJHcmFkaWVudD4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDFfbGluZWFyXzQxOThfMTEwNDkzIiB4MT0iNDUuMjc5NyIgeTE9Ijc0LjY2MzUiIHgyPSI0NS4yMjYxIiB5Mj0iLTIwLjI0MzEiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agb2Zmc2V0PSIwLjE4MjI1NCIgc3RvcC1jb2xvcj0iIzNGQTcxQSIvPgo8c3RvcCBvZmZzZXQ9IjAuMTkwMTE4IiBzdG9wLWNvbG9yPSIjRkZBNjAwIi8+CjxzdG9wIG9mZnNldD0iMC4zODI3NjEiIHN0b3AtY29sb3I9IiNGRkE2MDAiLz4KPHN0b3Agb2Zmc2V0PSIwLjM4ODI2OCIgc3RvcC1jb2xvcj0iI0Y1NkUwOCIvPgo8c3RvcCBvZmZzZXQ9IjAuNTgxNjk1IiBzdG9wLWNvbG9yPSIjRjU2RTA4Ii8+CjxzdG9wIG9mZnNldD0iMC41ODc5ODUiIHN0b3AtY29sb3I9IiNGRjRENUEiLz4KPC9saW5lYXJHcmFkaWVudD4KPGNsaXBQYXRoIGlkPSJjbGlwMF80MTk4XzExMDQ5MyI+CjxyZWN0IHdpZHRoPSI5NiIgaGVpZ2h0PSI3NiIgZmlsbD0id2hpdGUiLz4KPC9jbGlwUGF0aD4KPGNsaXBQYXRoIGlkPSJjbGlwMV80MTk4XzExMDQ5MyI+CjxyZWN0IHdpZHRoPSI5NiIgaGVpZ2h0PSI3NiIgZmlsbD0id2hpdGUiLz4KPC9jbGlwUGF0aD4KPGNsaXBQYXRoIGlkPSJjbGlwMl80MTk4XzExMDQ5MyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iNzYiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwIDg0KSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo=", "description": "Display time series data using customizable line and bar charts. Use various pie charts to display the latest values.", "order": 1000, "name": "Charts" }, "widgetTypeFqns": [ + "time_series_chart", + "line_chart", + "bar_chart", + "point_chart", "charts.basic_timeseries", "charts.state_chart", "range_chart", diff --git a/application/src/main/data/json/system/widget_types/bar_chart.json b/application/src/main/data/json/system/widget_types/bar_chart.json new file mode 100644 index 0000000000..c857922eea --- /dev/null +++ b/application/src/main/data/json/system/widget_types/bar_chart.json @@ -0,0 +1,32 @@ +{ + "fqn": "bar_chart", + "name": "Bar chart", + "deprecated": false, + "image": "tb-image:Y2hhcnRfKDIpLnN2Zw==:IkJhciBjaGFydCIgc3lzdGVtIHdpZGdldCBpbWFnZQ==;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF80MTgzXzkwODc3KSI+CjxwYXRoIGQ9Ik0yLjg0NzY2IDEuMjgxMjVWN0gyLjEyNVYyLjE4MzU5TDAuNjY3OTY5IDIuNzE0ODRWMi4wNjI1TDIuNzM0MzggMS4yODEyNUgyLjg0NzY2Wk04LjY3NTE3IDMuNzAzMTJWNC41NzAzMUM4LjY3NTE3IDUuMDM2NDYgOC42MzM1MSA1LjQyOTY5IDguNTUwMTcgNS43NUM4LjQ2Njg0IDYuMDcwMzEgOC4zNDcwNSA2LjMyODEyIDguMTkwOCA2LjUyMzQ0QzguMDM0NTUgNi43MTg3NSA3Ljg0NTc1IDYuODYwNjggNy42MjQzOSA2Ljk0OTIyQzcuNDA1NjQgNy4wMzUxNiA3LjE1ODI1IDcuMDc4MTIgNi44ODIyIDcuMDc4MTJDNi42NjM0NSA3LjA3ODEyIDYuNDYxNjMgNy4wNTA3OCA2LjI3NjczIDYuOTk2MDlDNi4wOTE4NCA2Ljk0MTQxIDUuOTI1MTcgNi44NTQxNyA1Ljc3NjczIDYuNzM0MzhDNS42MzA5IDYuNjExOTggNS41MDU5IDYuNDUzMTIgNS40MDE3MyA2LjI1NzgxQzUuMjk3NTcgNi4wNjI1IDUuMjE4MTQgNS44MjU1MiA1LjE2MzQ1IDUuNTQ2ODhDNS4xMDg3NyA1LjI2ODIzIDUuMDgxNDIgNC45NDI3MSA1LjA4MTQyIDQuNTcwMzFWMy43MDMxMkM1LjA4MTQyIDMuMjM2OTggNS4xMjMwOSAyLjg0NjM1IDUuMjA2NDIgMi41MzEyNUM1LjI5MjM2IDIuMjE2MTUgNS40MTM0NSAxLjk2MzU0IDUuNTY5NyAxLjc3MzQ0QzUuNzI1OTUgMS41ODA3MyA1LjkxMzQ1IDEuNDQyNzEgNi4xMzIyIDEuMzU5MzhDNi4zNTM1NiAxLjI3NjA0IDYuNjAwOTUgMS4yMzQzOCA2Ljg3NDM5IDEuMjM0MzhDNy4wOTU3NSAxLjIzNDM4IDcuMjk4ODcgMS4yNjE3MiA3LjQ4Mzc3IDEuMzE2NDFDNy42NzEyNyAxLjM2ODQ5IDcuODM3OTMgMS40NTMxMiA3Ljk4Mzc3IDEuNTcwMzFDOC4xMjk2IDEuNjg0OSA4LjI1MzMgMS44Mzg1NCA4LjM1NDg2IDIuMDMxMjVDOC40NTkwMyAyLjIyMTM1IDguNTM4NDUgMi40NTQ0MyA4LjU5MzE0IDIuNzMwNDdDOC42NDc4MyAzLjAwNjUxIDguNjc1MTcgMy4zMzA3MyA4LjY3NTE3IDMuNzAzMTJaTTcuOTQ4NjEgNC42ODc1VjMuNTgyMDNDNy45NDg2MSAzLjMyNjgyIDcuOTMyOTggMy4xMDI4NiA3LjkwMTczIDIuOTEwMTZDNy44NzMwOSAyLjcxNDg0IDcuODMwMTIgMi41NDgxOCA3Ljc3MjgzIDIuNDEwMTZDNy43MTU1NCAyLjI3MjE0IDcuNjQyNjIgMi4xNjAxNiA3LjU1NDA4IDIuMDc0MjJDNy40NjgxNCAxLjk4ODI4IDcuMzY3ODggMS45MjU3OCA3LjI1MzMgMS44ODY3MkM3LjE0MTMyIDEuODQ1MDUgNy4wMTUwMiAxLjgyNDIyIDYuODc0MzkgMS44MjQyMkM2LjcwMjUyIDEuODI0MjIgNi41NTAxNyAxLjg1Njc3IDYuNDE3MzYgMS45MjE4OEM2LjI4NDU1IDEuOTg0MzggNi4xNzI1NyAyLjA4NDY0IDYuMDgxNDIgMi4yMjI2NkM1Ljk5Mjg4IDIuMzYwNjggNS45MjUxNyAyLjU0MTY3IDUuODc4MyAyLjc2NTYyQzUuODMxNDIgMi45ODk1OCA1LjgwNzk4IDMuMjYxNzIgNS44MDc5OCAzLjU4MjAzVjQuNjg3NUM1LjgwNzk4IDQuOTQyNzEgNS44MjIzMSA1LjE2Nzk3IDUuODUwOTUgNS4zNjMyOEM1Ljg4MjIgNS41NTg1OSA1LjkyNzc4IDUuNzI3ODYgNS45ODc2NyA1Ljg3MTA5QzYuMDQ3NTcgNi4wMTE3MiA2LjEyMDQ4IDYuMTI3NiA2LjIwNjQyIDYuMjE4NzVDNi4yOTIzNiA2LjMwOTkgNi4zOTEzMiA2LjM3NzYgNi41MDMzIDYuNDIxODhDNi42MTc4OCA2LjQ2MzU0IDYuNzQ0MTggNi40ODQzOCA2Ljg4MjIgNi40ODQzOEM3LjA1OTI5IDYuNDg0MzggNy4yMTQyMyA2LjQ1MDUyIDcuMzQ3MDUgNi4zODI4MUM3LjQ3OTg2IDYuMzE1MSA3LjU5MDU0IDYuMjA5NjQgNy42NzkwOCA2LjA2NjQxQzcuNzcwMjIgNS45MjA1NyA3LjgzNzkzIDUuNzM0MzggNy44ODIyIDUuNTA3ODFDNy45MjY0NyA1LjI3ODY1IDcuOTQ4NjEgNS4wMDUyMSA3Ljk0ODYxIDQuNjg3NVpNMTMuMzA3NCAzLjcwMzEyVjQuNTcwMzFDMTMuMzA3NCA1LjAzNjQ2IDEzLjI2NTcgNS40Mjk2OSAxMy4xODI0IDUuNzVDMTMuMDk5IDYuMDcwMzEgMTIuOTc5MyA2LjMyODEyIDEyLjgyMyA2LjUyMzQ0QzEyLjY2NjggNi43MTg3NSAxMi40Nzc5IDYuODYwNjggMTIuMjU2NiA2Ljk0OTIyQzEyLjAzNzggNy4wMzUxNiAxMS43OTA0IDcuMDc4MTIgMTEuNTE0NCA3LjA3ODEyQzExLjI5NTcgNy4wNzgxMiAxMS4wOTM4IDcuMDUwNzggMTAuOTA4OSA2Ljk5NjA5QzEwLjcyNCA2Ljk0MTQxIDEwLjU1NzQgNi44NTQxNyAxMC40MDg5IDYuNzM0MzhDMTAuMjYzMSA2LjYxMTk4IDEwLjEzODEgNi40NTMxMiAxMC4wMzM5IDYuMjU3ODFDOS45Mjk3NyA2LjA2MjUgOS44NTAzNCA1LjgyNTUyIDkuNzk1NjYgNS41NDY4OEM5Ljc0MDk3IDUuMjY4MjMgOS43MTM2MyA0Ljk0MjcxIDkuNzEzNjMgNC41NzAzMVYzLjcwMzEyQzkuNzEzNjMgMy4yMzY5OCA5Ljc1NTI5IDIuODQ2MzUgOS44Mzg2MyAyLjUzMTI1QzkuOTI0NTYgMi4yMTYxNSAxMC4wNDU3IDEuOTYzNTQgMTAuMjAxOSAxLjc3MzQ0QzEwLjM1ODIgMS41ODA3MyAxMC41NDU3IDEuNDQyNzEgMTAuNzY0NCAxLjM1OTM4QzEwLjk4NTggMS4yNzYwNCAxMS4yMzMyIDEuMjM0MzggMTEuNTA2NiAxLjIzNDM4QzExLjcyNzkgMS4yMzQzOCAxMS45MzExIDEuMjYxNzIgMTIuMTE2IDEuMzE2NDFDMTIuMzAzNSAxLjM2ODQ5IDEyLjQ3MDEgMS40NTMxMiAxMi42MTYgMS41NzAzMUMxMi43NjE4IDEuNjg0OSAxMi44ODU1IDEuODM4NTQgMTIuOTg3MSAyLjAzMTI1QzEzLjA5MTIgMi4yMjEzNSAxMy4xNzA3IDIuNDU0NDMgMTMuMjI1MyAyLjczMDQ3QzEzLjI4IDMuMDA2NTEgMTMuMzA3NCAzLjMzMDczIDEzLjMwNzQgMy43MDMxMlpNMTIuNTgwOCA0LjY4NzVWMy41ODIwM0MxMi41ODA4IDMuMzI2ODIgMTIuNTY1MiAzLjEwMjg2IDEyLjUzMzkgMi45MTAxNkMxMi41MDUzIDIuNzE0ODQgMTIuNDYyMyAyLjU0ODE4IDEyLjQwNSAyLjQxMDE2QzEyLjM0NzcgMi4yNzIxNCAxMi4yNzQ4IDIuMTYwMTYgMTIuMTg2MyAyLjA3NDIyQzEyLjEwMDMgMS45ODgyOCAxMi4wMDAxIDEuOTI1NzggMTEuODg1NSAxLjg4NjcyQzExLjc3MzUgMS44NDUwNSAxMS42NDcyIDEuODI0MjIgMTEuNTA2NiAxLjgyNDIyQzExLjMzNDcgMS44MjQyMiAxMS4xODI0IDEuODU2NzcgMTEuMDQ5NiAxLjkyMTg4QzEwLjkxNjggMS45ODQzOCAxMC44MDQ4IDIuMDg0NjQgMTAuNzEzNiAyLjIyMjY2QzEwLjYyNTEgMi4zNjA2OCAxMC41NTc0IDIuNTQxNjcgMTAuNTEwNSAyLjc2NTYyQzEwLjQ2MzYgMi45ODk1OCAxMC40NDAyIDMuMjYxNzIgMTAuNDQwMiAzLjU4MjAzVjQuNjg3NUMxMC40NDAyIDQuOTQyNzEgMTAuNDU0NSA1LjE2Nzk3IDEwLjQ4MzIgNS4zNjMyOEMxMC41MTQ0IDUuNTU4NTkgMTAuNTYgNS43Mjc4NiAxMC42MTk5IDUuODcxMDlDMTAuNjc5OCA2LjAxMTcyIDEwLjc1MjcgNi4xMjc2IDEwLjgzODYgNi4yMTg3NUMxMC45MjQ2IDYuMzA5OSAxMS4wMjM1IDYuMzc3NiAxMS4xMzU1IDYuNDIxODhDMTEuMjUwMSA2LjQ2MzU0IDExLjM3NjQgNi40ODQzOCAxMS41MTQ0IDYuNDg0MzhDMTEuNjkxNSA2LjQ4NDM4IDExLjg0NjQgNi40NTA1MiAxMS45NzkzIDYuMzgyODFDMTIuMTEyMSA2LjMxNTEgMTIuMjIyNyA2LjIwOTY0IDEyLjMxMTMgNi4wNjY0MUMxMi40MDI0IDUuOTIwNTcgMTIuNDcwMSA1LjczNDM4IDEyLjUxNDQgNS41MDc4MUMxMi41NTg3IDUuMjc4NjUgMTIuNTgwOCA1LjAwNTIxIDEyLjU4MDggNC42ODc1Wk0xNC4zMDY4IDIuNzA3MDNWMi40MDYyNUMxNC4zMDY4IDIuMTkwMSAxNC4zNTM2IDEuOTkzNDkgMTQuNDQ3NCAxLjgxNjQxQzE0LjU0MTEgMS42MzkzMiAxNC42NzUzIDEuNDk3NCAxNC44NDk3IDEuMzkwNjJDMTUuMDI0MiAxLjI4Mzg1IDE1LjIzMTIgMS4yMzA0NyAxNS40NzA4IDEuMjMwNDdDMTUuNzE1NiAxLjIzMDQ3IDE1LjkyNCAxLjI4Mzg1IDE2LjA5NTggMS4zOTA2MkMxNi4yNzAzIDEuNDk3NCAxNi40MDQ0IDEuNjM5MzIgMTYuNDk4MiAxLjgxNjQxQzE2LjU5MTkgMS45OTM0OSAxNi42Mzg4IDIuMTkwMSAxNi42Mzg4IDIuNDA2MjVWMi43MDcwM0MxNi42Mzg4IDIuOTE3OTcgMTYuNTkxOSAzLjExMTk4IDE2LjQ5ODIgMy4yODkwNkMxNi40MDcgMy40NjYxNSAxNi4yNzQyIDMuNjA4MDcgMTYuMDk5NyAzLjcxNDg0QzE1LjkyNzkgMy44MjE2MSAxNS43MjA4IDMuODc1IDE1LjQ3ODYgMy44NzVDMTUuMjM2NSAzLjg3NSAxNS4wMjY4IDMuODIxNjEgMTQuODQ5NyAzLjcxNDg0QzE0LjY3NTMgMy42MDgwNyAxNC41NDExIDMuNDY2MTUgMTQuNDQ3NCAzLjI4OTA2QzE0LjM1MzYgMy4xMTE5OCAxNC4zMDY4IDIuOTE3OTcgMTQuMzA2OCAyLjcwNzAzWk0xNC44NDk3IDIuNDA2MjVWMi43MDcwM0MxNC44NDk3IDIuODI2ODIgMTQuODcxOSAyLjk0MDEgMTQuOTE2MSAzLjA0Njg4QzE0Ljk2MyAzLjE1MzY1IDE1LjAzMzMgMy4yNDA4OSAxNS4xMjcxIDMuMzA4NTlDMTUuMjIwOCAzLjM3MzcgMTUuMzM4IDMuNDA2MjUgMTUuNDc4NiAzLjQwNjI1QzE1LjYxOTMgMy40MDYyNSAxNS43MzUyIDMuMzczNyAxNS44MjYzIDMuMzA4NTlDMTUuOTE3NCAzLjI0MDg5IDE1Ljk4NTIgMy4xNTM2NSAxNi4wMjk0IDMuMDQ2ODhDMTYuMDczNyAyLjk0MDEgMTYuMDk1OCAyLjgyNjgyIDE2LjA5NTggMi43MDcwM1YyLjQwNjI1QzE2LjA5NTggMi4yODM4NSAxNi4wNzI0IDIuMTY5MjcgMTYuMDI1NSAyLjA2MjVDMTUuOTgxMiAxLjk1MzEyIDE1LjkxMjIgMS44NjU4OSAxNS44MTg1IDEuODAwNzhDMTUuNzI3MyAxLjczMzA3IDE1LjYxMTUgMS42OTkyMiAxNS40NzA4IDEuNjk5MjJDMTUuMzMyOCAxLjY5OTIyIDE1LjIxNjkgMS43MzMwNyAxNS4xMjMyIDEuODAwNzhDMTUuMDMyIDEuODY1ODkgMTQuOTYzIDEuOTUzMTIgMTQuOTE2MSAyLjA2MjVDMTQuODcxOSAyLjE2OTI3IDE0Ljg0OTcgMi4yODM4NSAxNC44NDk3IDIuNDA2MjVaTTE3LjA3NjMgNS45MTAxNlY1LjYwNTQ3QzE3LjA3NjMgNS4zOTE5MyAxNy4xMjMyIDUuMTk2NjEgMTcuMjE2OSA1LjAxOTUzQzE3LjMxMDcgNC44NDI0NSAxNy40NDQ4IDQuNzAwNTIgMTcuNjE5MyA0LjU5Mzc1QzE3Ljc5MzcgNC40ODY5OCAxOC4wMDA4IDQuNDMzNTkgMTguMjQwNCA0LjQzMzU5QzE4LjQ4NTIgNC40MzM1OSAxOC42OTM1IDQuNDg2OTggMTguODY1NCA0LjU5Mzc1QzE5LjAzOTggNC43MDA1MiAxOS4xNzQgNC44NDI0NSAxOS4yNjc3IDUuMDE5NTNDMTkuMzYxNSA1LjE5NjYxIDE5LjQwODMgNS4zOTE5MyAxOS40MDgzIDUuNjA1NDdWNS45MTAxNkMxOS40MDgzIDYuMTIzNyAxOS4zNjE1IDYuMzE5MDEgMTkuMjY3NyA2LjQ5NjA5QzE5LjE3NjYgNi42NzMxOCAxOS4wNDM3IDYuODE1MSAxOC44NjkzIDYuOTIxODhDMTguNjk3NCA3LjAyODY1IDE4LjQ5MDQgNy4wODIwMyAxOC4yNDgyIDcuMDgyMDNDMTguMDA2IDcuMDgyMDMgMTcuNzk3NyA3LjAyODY1IDE3LjYyMzIgNi45MjE4OEMxNy40NDg3IDYuODE1MSAxNy4zMTMzIDYuNjczMTggMTcuMjE2OSA2LjQ5NjA5QzE3LjEyMzIgNi4zMTkwMSAxNy4wNzYzIDYuMTIzNyAxNy4wNzYzIDUuOTEwMTZaTTE3LjYxOTMgNS42MDU0N1Y1LjkxMDE2QzE3LjYxOTMgNi4wMjk5NSAxNy42NDE0IDYuMTQ0NTMgMTcuNjg1NyA2LjI1MzkxQzE3LjczMjUgNi4zNjA2OCAxNy44MDI5IDYuNDQ3OTIgMTcuODk2NiA2LjUxNTYyQzE3Ljk5MDQgNi41ODA3MyAxOC4xMDc1IDYuNjEzMjggMTguMjQ4MiA2LjYxMzI4QzE4LjM4ODggNi42MTMyOCAxOC41MDQ3IDYuNTgwNzMgMTguNTk1OCA2LjUxNTYyQzE4LjY4OTYgNi40NDc5MiAxOC43NTg2IDYuMzYwNjggMTguODAyOSA2LjI1MzkxQzE4Ljg0NzEgNi4xNDcxNCAxOC44NjkzIDYuMDMyNTUgMTguODY5MyA1LjkxMDE2VjUuNjA1NDdDMTguODY5MyA1LjQ4MzA3IDE4Ljg0NTggNS4zNjg0OSAxOC43OTkgNS4yNjE3MkMxOC43NTQ3IDUuMTU0OTUgMTguNjg1NyA1LjA2OTAxIDE4LjU5MTkgNS4wMDM5MUMxOC41MDA4IDQuOTM2MiAxOC4zODM2IDQuOTAyMzQgMTguMjQwNCA0LjkwMjM0QzE4LjEwMjMgNC45MDIzNCAxNy45ODY1IDQuOTM2MiAxNy44OTI3IDUuMDAzOTFDMTcuODAxNiA1LjA2OTAxIDE3LjczMjUgNS4xNTQ5NSAxNy42ODU3IDUuMjYxNzJDMTcuNjQxNCA1LjM2ODQ5IDE3LjYxOTMgNS40ODMwNyAxNy42MTkzIDUuNjA1NDdaTTE4LjQyIDIuMTIxMDlMMTUuNjQyNyA2LjU2NjQxTDE1LjIzNjUgNi4zMDg1OUwxOC4wMTM4IDEuODYzMjhMMTguNDIgMi4xMjEwOVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPHBhdGggZD0iTTguMDU4NTkgMzMuNjY3N0M4LjA1ODU5IDM0LjAxNDEgNy45Nzc4NiAzNC4zMDgzIDcuODE2NDEgMzQuNTUwNUM3LjY1NzU1IDM0Ljc5MDEgNy40NDE0MSAzNC45NzI0IDcuMTY3OTcgMzUuMDk3NEM2Ljg5NzE0IDM1LjIyMjQgNi41OTExNSAzNS4yODQ5IDYuMjUgMzUuMjg0OUM1LjkwODg1IDM1LjI4NDkgNS42MDE1NiAzNS4yMjI0IDUuMzI4MTIgMzUuMDk3NEM1LjA1NDY5IDM0Ljk3MjQgNC44Mzg1NCAzNC43OTAxIDQuNjc5NjkgMzQuNTUwNUM0LjUyMDgzIDM0LjMwODMgNC40NDE0MSAzNC4wMTQxIDQuNDQxNDEgMzMuNjY3N0M0LjQ0MTQxIDMzLjQ0MTIgNC40ODQzOCAzMy4yMzQxIDQuNTcwMzEgMzMuMDQ2NkM0LjY1ODg1IDMyLjg1NjUgNC43ODI1NSAzMi42OTEyIDQuOTQxNDEgMzIuNTUwNUM1LjEwMjg2IDMyLjQwOTkgNS4yOTI5NyAzMi4zMDE4IDUuNTExNzIgMzIuMjI2M0M1LjczMzA3IDMyLjE0ODIgNS45NzY1NiAzMi4xMDkxIDYuMjQyMTkgMzIuMTA5MUM2LjU5MTE1IDMyLjEwOTEgNi45MDIzNCAzMi4xNzY4IDcuMTc1NzggMzIuMzEyM0M3LjQ0OTIyIDMyLjQ0NTEgNy42NjQwNiAzMi42Mjg3IDcuODIwMzEgMzIuODYzQzcuOTc5MTcgMzMuMDk3NCA4LjA1ODU5IDMzLjM2NTYgOC4wNTg1OSAzMy42Njc3Wk03LjMzMjAzIDMzLjY1MjFDNy4zMzIwMyAzMy40NDEyIDcuMjg2NDYgMzMuMjU1IDcuMTk1MzEgMzMuMDkzNUM3LjEwNDE3IDMyLjkyOTQgNi45NzY1NiAzMi44MDE4IDYuODEyNSAzMi43MTA3QzYuNjQ4NDQgMzIuNjE5NSA2LjQ1ODMzIDMyLjU3NCA2LjI0MjE5IDMyLjU3NEM2LjAyMDgzIDMyLjU3NCA1LjgyOTQzIDMyLjYxOTUgNS42Njc5NyAzMi43MTA3QzUuNTA5MTEgMzIuODAxOCA1LjM4NTQyIDMyLjkyOTQgNS4yOTY4OCAzMy4wOTM1QzUuMjA4MzMgMzMuMjU1IDUuMTY0MDYgMzMuNDQxMiA1LjE2NDA2IDMzLjY1MjFDNS4xNjQwNiAzMy44NzA4IDUuMjA3MDMgMzQuMDU4MyA1LjI5Mjk3IDM0LjIxNDZDNS4zODE1MSAzNC4zNjgyIDUuNTA2NTEgMzQuNDg2NyA1LjY2Nzk3IDM0LjU3MDFDNS44MzIwMyAzNC42NTA4IDYuMDI2MDQgMzQuNjkxMiA2LjI1IDM0LjY5MTJDNi40NzM5NiAzNC42OTEyIDYuNjY2NjcgMzQuNjUwOCA2LjgyODEyIDM0LjU3MDFDNi45ODk1OCAzNC40ODY3IDcuMTEzMjggMzQuMzY4MiA3LjE5OTIyIDM0LjIxNDZDNy4yODc3NiAzNC4wNTgzIDcuMzMyMDMgMzMuODcwOCA3LjMzMjAzIDMzLjY1MjFaTTcuOTI1NzggMzAuOTk5OEM3LjkyNTc4IDMxLjI3NTggNy44NTI4NiAzMS41MjQ1IDcuNzA3MDMgMzEuNzQ1OEM3LjU2MTIgMzEuOTY3MiA3LjM2MTk4IDMyLjE0MTcgNy4xMDkzOCAzMi4yNjkzQzYuODU2NzcgMzIuMzk2OSA2LjU3MDMxIDMyLjQ2MDcgNi4yNSAzMi40NjA3QzUuOTI0NDggMzIuNDYwNyA1LjYzNDExIDMyLjM5NjkgNS4zNzg5MSAzMi4yNjkzQzUuMTI2MyAzMi4xNDE3IDQuOTI4MzkgMzEuOTY3MiA0Ljc4NTE2IDMxLjc0NThDNC42NDE5MyAzMS41MjQ1IDQuNTcwMzEgMzEuMjc1OCA0LjU3MDMxIDMwLjk5OThDNC41NzAzMSAzMC42NjkgNC42NDE5MyAzMC4zODc4IDQuNzg1MTYgMzAuMTU2QzQuOTMwOTkgMjkuOTI0MiA1LjEzMDIxIDI5Ljc0NzIgNS4zODI4MSAyOS42MjQ4QzUuNjM1NDIgMjkuNTAyNCA1LjkyMzE4IDI5LjQ0MTIgNi4yNDYwOSAyOS40NDEyQzYuNTcxNjEgMjkuNDQxMiA2Ljg2MDY4IDI5LjUwMjQgNy4xMTMyOCAyOS42MjQ4QzcuMzY1ODkgMjkuNzQ3MiA3LjU2MzggMjkuOTI0MiA3LjcwNzAzIDMwLjE1NkM3Ljg1Mjg2IDMwLjM4NzggNy45MjU3OCAzMC42NjkgNy45MjU3OCAzMC45OTk4Wk03LjIwMzEyIDMxLjAxMTVDNy4yMDMxMiAzMC44MjE0IDcuMTYyNzYgMzAuNjUzNCA3LjA4MjAzIDMwLjUwNzZDNy4wMDEzIDMwLjM2MTcgNi44ODkzMiAzMC4yNDcyIDYuNzQ2MDkgMzAuMTYzOEM2LjYwMjg2IDMwLjA3NzkgNi40MzYyIDMwLjAzNDkgNi4yNDYwOSAzMC4wMzQ5QzYuMDU1OTkgMzAuMDM0OSA1Ljg4OTMyIDMwLjA3NTMgNS43NDYwOSAzMC4xNTZDNS42MDU0NyAzMC4yMzQxIDUuNDk0NzkgMzAuMzQ2MSA1LjQxNDA2IDMwLjQ5MTlDNS4zMzU5NCAzMC42Mzc4IDUuMjk2ODggMzAuODExIDUuMjk2ODggMzEuMDExNUM1LjI5Njg4IDMxLjIwNjggNS4zMzU5NCAzMS4zNzc0IDUuNDE0MDYgMzEuNTIzMkM1LjQ5NDc5IDMxLjY2OSA1LjYwNjc3IDMxLjc4MjMgNS43NSAzMS44NjNDNS44OTMyMyAzMS45NDM4IDYuMDU5OSAzMS45ODQxIDYuMjUgMzEuOTg0MUM2LjQ0MDEgMzEuOTg0MSA2LjYwNTQ3IDMxLjk0MzggNi43NDYwOSAzMS44NjNDNi44ODkzMiAzMS43ODIzIDcuMDAxMyAzMS42NjkgNy4wODIwMyAzMS41MjMyQzcuMTYyNzYgMzEuMzc3NCA3LjIwMzEyIDMxLjIwNjggNy4yMDMxMiAzMS4wMTE1Wk0xMi42NzUyIDMxLjkwOTlWMzIuNzc3MUMxMi42NzUyIDMzLjI0MzIgMTIuNjMzNSAzMy42MzY1IDEyLjU1MDIgMzMuOTU2OEMxMi40NjY4IDM0LjI3NzEgMTIuMzQ3IDM0LjUzNDkgMTIuMTkwOCAzNC43MzAyQzEyLjAzNDUgMzQuOTI1NSAxMS44NDU3IDM1LjA2NzUgMTEuNjI0NCAzNS4xNTZDMTEuNDA1NiAzNS4yNDE5IDExLjE1ODIgMzUuMjg0OSAxMC44ODIyIDM1LjI4NDlDMTAuNjYzNSAzNS4yODQ5IDEwLjQ2MTYgMzUuMjU3NiAxMC4yNzY3IDM1LjIwMjlDMTAuMDkxOCAzNS4xNDgyIDkuOTI1MTcgMzUuMDYxIDkuNzc2NzMgMzQuOTQxMkM5LjYzMDkgMzQuODE4OCA5LjUwNTkgMzQuNjU5OSA5LjQwMTczIDM0LjQ2NDZDOS4yOTc1NyAzNC4yNjkzIDkuMjE4MTQgMzQuMDMyMyA5LjE2MzQ1IDMzLjc1MzdDOS4xMDg3NyAzMy40NzUgOS4wODE0MiAzMy4xNDk1IDkuMDgxNDIgMzIuNzc3MVYzMS45MDk5QzkuMDgxNDIgMzEuNDQzOCA5LjEyMzA5IDMxLjA1MzEgOS4yMDY0MiAzMC43MzhDOS4yOTIzNiAzMC40MjI5IDkuNDEzNDUgMzAuMTcwMyA5LjU2OTcgMjkuOTgwMkM5LjcyNTk1IDI5Ljc4NzUgOS45MTM0NSAyOS42NDk1IDEwLjEzMjIgMjkuNTY2MkMxMC4zNTM2IDI5LjQ4MjggMTAuNjAxIDI5LjQ0MTIgMTAuODc0NCAyOS40NDEyQzExLjA5NTcgMjkuNDQxMiAxMS4yOTg5IDI5LjQ2ODUgMTEuNDgzOCAyOS41MjMyQzExLjY3MTMgMjkuNTc1MyAxMS44Mzc5IDI5LjY1OTkgMTEuOTgzOCAyOS43NzcxQzEyLjEyOTYgMjkuODkxNyAxMi4yNTMzIDMwLjA0NTMgMTIuMzU0OSAzMC4yMzhDMTIuNDU5IDMwLjQyODEgMTIuNTM4NSAzMC42NjEyIDEyLjU5MzEgMzAuOTM3M0MxMi42NDc4IDMxLjIxMzMgMTIuNjc1MiAzMS41Mzc1IDEyLjY3NTIgMzEuOTA5OVpNMTEuOTQ4NiAzMi44OTQzVjMxLjc4ODhDMTEuOTQ4NiAzMS41MzM2IDExLjkzMyAzMS4zMDk3IDExLjkwMTcgMzEuMTE2OUMxMS44NzMxIDMwLjkyMTYgMTEuODMwMSAzMC43NTUgMTEuNzcyOCAzMC42MTY5QzExLjcxNTUgMzAuNDc4OSAxMS42NDI2IDMwLjM2NjkgMTEuNTU0MSAzMC4yODFDMTEuNDY4MSAzMC4xOTUxIDExLjM2NzkgMzAuMTMyNiAxMS4yNTMzIDMwLjA5MzVDMTEuMTQxMyAzMC4wNTE4IDExLjAxNSAzMC4wMzEgMTAuODc0NCAzMC4wMzFDMTAuNzAyNSAzMC4wMzEgMTAuNTUwMiAzMC4wNjM2IDEwLjQxNzQgMzAuMTI4N0MxMC4yODQ1IDMwLjE5MTIgMTAuMTcyNiAzMC4yOTE0IDEwLjA4MTQgMzAuNDI5NEM5Ljk5Mjg4IDMwLjU2NzUgOS45MjUxNyAzMC43NDg1IDkuODc4MyAzMC45NzI0QzkuODMxNDIgMzEuMTk2NCA5LjgwNzk4IDMxLjQ2ODUgOS44MDc5OCAzMS43ODg4VjMyLjg5NDNDOS44MDc5OCAzMy4xNDk1IDkuODIyMzEgMzMuMzc0OCA5Ljg1MDk1IDMzLjU3MDFDOS44ODIyIDMzLjc2NTQgOS45Mjc3OCAzMy45MzQ3IDkuOTg3NjcgMzQuMDc3OUMxMC4wNDc2IDM0LjIxODUgMTAuMTIwNSAzNC4zMzQ0IDEwLjIwNjQgMzQuNDI1NUMxMC4yOTI0IDM0LjUxNjcgMTAuMzkxMyAzNC41ODQ0IDEwLjUwMzMgMzQuNjI4N0MxMC42MTc5IDM0LjY3MDMgMTAuNzQ0MiAzNC42OTEyIDEwLjg4MjIgMzQuNjkxMkMxMS4wNTkzIDM0LjY5MTIgMTEuMjE0MiAzNC42NTczIDExLjM0NyAzNC41ODk2QzExLjQ3OTkgMzQuNTIxOSAxMS41OTA1IDM0LjQxNjQgMTEuNjc5MSAzNC4yNzMyQzExLjc3MDIgMzQuMTI3NCAxMS44Mzc5IDMzLjk0MTIgMTEuODgyMiAzMy43MTQ2QzExLjkyNjUgMzMuNDg1NCAxMS45NDg2IDMzLjIxMiAxMS45NDg2IDMyLjg5NDNaTTEzLjY3NDYgMzAuOTEzOFYzMC42MTNDMTMuNjc0NiAzMC4zOTY5IDEzLjcyMTQgMzAuMjAwMyAxMy44MTUyIDMwLjAyMzJDMTMuOTA4OSAyOS44NDYxIDE0LjA0MzEgMjkuNzA0MiAxNC4yMTc1IDI5LjU5NzRDMTQuMzkyIDI5LjQ5MDYgMTQuNTk5IDI5LjQzNzMgMTQuODM4NiAyOS40MzczQzE1LjA4MzQgMjkuNDM3MyAxNS4yOTE4IDI5LjQ5MDYgMTUuNDYzNiAyOS41OTc0QzE1LjYzODEgMjkuNzA0MiAxNS43NzIyIDI5Ljg0NjEgMTUuODY2IDMwLjAyMzJDMTUuOTU5NyAzMC4yMDAzIDE2LjAwNjYgMzAuMzk2OSAxNi4wMDY2IDMwLjYxM1YzMC45MTM4QzE2LjAwNjYgMzEuMTI0OCAxNS45NTk3IDMxLjMxODggMTUuODY2IDMxLjQ5NThDMTUuNzc0OCAzMS42NzI5IDE1LjY0MiAzMS44MTQ5IDE1LjQ2NzUgMzEuOTIxNkMxNS4yOTU3IDMyLjAyODQgMTUuMDg4NiAzMi4wODE4IDE0Ljg0NjQgMzIuMDgxOEMxNC42MDQzIDMyLjA4MTggMTQuMzk0NiAzMi4wMjg0IDE0LjIxNzUgMzEuOTIxNkMxNC4wNDMxIDMxLjgxNDkgMTMuOTA4OSAzMS42NzI5IDEzLjgxNTIgMzEuNDk1OEMxMy43MjE0IDMxLjMxODggMTMuNjc0NiAzMS4xMjQ4IDEzLjY3NDYgMzAuOTEzOFpNMTQuMjE3NSAzMC42MTNWMzAuOTEzOEMxNC4yMTc1IDMxLjAzMzYgMTQuMjM5NyAzMS4xNDY5IDE0LjI4MzkgMzEuMjUzN0MxNC4zMzA4IDMxLjM2MDQgMTQuNDAxMSAzMS40NDc3IDE0LjQ5NDkgMzEuNTE1NEMxNC41ODg2IDMxLjU4MDUgMTQuNzA1OCAzMS42MTMgMTQuODQ2NCAzMS42MTNDMTQuOTg3MSAzMS42MTMgMTUuMTAyOSAzMS41ODA1IDE1LjE5NDEgMzEuNTE1NEMxNS4yODUyIDMxLjQ0NzcgMTUuMzUyOSAzMS4zNjA0IDE1LjM5NzIgMzEuMjUzN0MxNS40NDE1IDMxLjE0NjkgMTUuNDYzNiAzMS4wMzM2IDE1LjQ2MzYgMzAuOTEzOFYzMC42MTNDMTUuNDYzNiAzMC40OTA2IDE1LjQ0MDIgMzAuMzc2MSAxNS4zOTMzIDMwLjI2OTNDMTUuMzQ5IDMwLjE1OTkgMTUuMjggMzAuMDcyNyAxNS4xODYzIDMwLjAwNzZDMTUuMDk1MSAyOS45Mzk5IDE0Ljk3OTMgMjkuOTA2IDE0LjgzODYgMjkuOTA2QzE0LjcwMDYgMjkuOTA2IDE0LjU4NDcgMjkuOTM5OSAxNC40OTEgMzAuMDA3NkMxNC4zOTk4IDMwLjA3MjcgMTQuMzMwOCAzMC4xNTk5IDE0LjI4MzkgMzAuMjY5M0MxNC4yMzk3IDMwLjM3NjEgMTQuMjE3NSAzMC40OTA2IDE0LjIxNzUgMzAuNjEzWk0xNi40NDQxIDM0LjExNjlWMzMuODEyM0MxNi40NDQxIDMzLjU5ODcgMTYuNDkxIDMzLjQwMzQgMTYuNTg0NyAzMy4yMjYzQzE2LjY3ODUgMzMuMDQ5MiAxNi44MTI2IDMyLjkwNzMgMTYuOTg3MSAzMi44MDA1QzE3LjE2MTUgMzIuNjkzOCAxNy4zNjg2IDMyLjY0MDQgMTcuNjA4MiAzMi42NDA0QzE3Ljg1MjkgMzIuNjQwNCAxOC4wNjEzIDMyLjY5MzggMTguMjMzMiAzMi44MDA1QzE4LjQwNzYgMzIuOTA3MyAxOC41NDE4IDMzLjA0OTIgMTguNjM1NSAzMy4yMjYzQzE4LjcyOTMgMzMuNDAzNCAxOC43NzYxIDMzLjU5ODcgMTguNzc2MSAzMy44MTIzVjM0LjExNjlDMTguNzc2MSAzNC4zMzA1IDE4LjcyOTMgMzQuNTI1OCAxOC42MzU1IDM0LjcwMjlDMTguNTQ0NCAzNC44OCAxOC40MTE1IDM1LjAyMTkgMTguMjM3MSAzNS4xMjg3QzE4LjA2NTIgMzUuMjM1NCAxNy44NTgyIDM1LjI4ODggMTcuNjE2IDM1LjI4ODhDMTcuMzczOCAzNS4yODg4IDE3LjE2NTQgMzUuMjM1NCAxNi45OTEgMzUuMTI4N0MxNi44MTY1IDM1LjAyMTkgMTYuNjgxMSAzNC44OCAxNi41ODQ3IDM0LjcwMjlDMTYuNDkxIDM0LjUyNTggMTYuNDQ0MSAzNC4zMzA1IDE2LjQ0NDEgMzQuMTE2OVpNMTYuOTg3MSAzMy44MTIzVjM0LjExNjlDMTYuOTg3MSAzNC4yMzY3IDE3LjAwOTIgMzQuMzUxMyAxNy4wNTM1IDM0LjQ2MDdDMTcuMTAwMyAzNC41Njc1IDE3LjE3MDcgMzQuNjU0NyAxNy4yNjQ0IDM0LjcyMjRDMTcuMzU4MiAzNC43ODc1IDE3LjQ3NTMgMzQuODIwMSAxNy42MTYgMzQuODIwMUMxNy43NTY2IDM0LjgyMDEgMTcuODcyNSAzNC43ODc1IDE3Ljk2MzYgMzQuNzIyNEMxOC4wNTc0IDM0LjY1NDcgMTguMTI2NCAzNC41Njc1IDE4LjE3MDcgMzQuNDYwN0MxOC4yMTQ5IDM0LjM1MzkgMTguMjM3MSAzNC4yMzkzIDE4LjIzNzEgMzQuMTE2OVYzMy44MTIzQzE4LjIzNzEgMzMuNjg5OSAxOC4yMTM2IDMzLjU3NTMgMTguMTY2OCAzMy40Njg1QzE4LjEyMjUgMzMuMzYxNyAxOC4wNTM1IDMzLjI3NTggMTcuOTU5NyAzMy4yMTA3QzE3Ljg2ODYgMzMuMTQzIDE3Ljc1MTQgMzMuMTA5MSAxNy42MDgyIDMzLjEwOTFDMTcuNDcwMSAzMy4xMDkxIDE3LjM1NDMgMzMuMTQzIDE3LjI2MDUgMzMuMjEwN0MxNy4xNjk0IDMzLjI3NTggMTcuMTAwMyAzMy4zNjE3IDE3LjA1MzUgMzMuNDY4NUMxNy4wMDkyIDMzLjU3NTMgMTYuOTg3MSAzMy42ODk5IDE2Ljk4NzEgMzMuODEyM1pNMTcuNzg3OCAzMC4zMjc5TDE1LjAxMDUgMzQuNzczMkwxNC42MDQzIDM0LjUxNTRMMTcuMzgxNiAzMC4wNzAxTDE3Ljc4NzggMzAuMzI3OVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPHBhdGggZD0iTTcuMjQ2MDkgNTcuNzE4M0g3LjMwODU5VjU4LjMzMTVINy4yNDYwOUM2Ljg2MzI4IDU4LjMzMTUgNi41NDI5NyA1OC4zOTQgNi4yODUxNiA1OC41MTlDNi4wMjczNCA1OC42NDE0IDUuODIyOTIgNTguODA2OCA1LjY3MTg4IDU5LjAxNTFDNS41MjA4MyA1OS4yMjA5IDUuNDExNDYgNTkuNDUyNiA1LjM0Mzc1IDU5LjcxMDRDNS4yNzg2NSA1OS45NjgzIDUuMjQ2MDkgNjAuMjMgNS4yNDYwOSA2MC40OTU2VjYxLjMzMTVDNS4yNDYwOSA2MS41ODQxIDUuMjc2MDQgNjEuODA4MSA1LjMzNTk0IDYyLjAwMzRDNS4zOTU4MyA2Mi4xOTYxIDUuNDc3ODYgNjIuMzU4OSA1LjU4MjAzIDYyLjQ5MTdDNS42ODYyIDYyLjYyNDUgNS44MDMzOSA2Mi43MjQ4IDUuOTMzNTkgNjIuNzkyNUM2LjA2NjQxIDYyLjg2MDIgNi4yMDQ0MyA2Mi44OTQgNi4zNDc2NiA2Mi44OTRDNi41MTQzMiA2Mi44OTQgNi42NjI3NiA2Mi44NjI4IDYuNzkyOTcgNjIuODAwM0M2LjkyMzE4IDYyLjczNTIgNy4wMzI1NSA2Mi42NDUzIDcuMTIxMDkgNjIuNTMwOEM3LjIxMjI0IDYyLjQxMzYgNy4yODEyNSA2Mi4yNzU2IDcuMzI4MTIgNjIuMTE2N0M3LjM3NSA2MS45NTc4IDcuMzk4NDQgNjEuNzgzNCA3LjM5ODQ0IDYxLjU5MzNDNy4zOTg0NCA2MS40MjQgNy4zNzc2IDYxLjI2MTIgNy4zMzU5NCA2MS4xMDVDNy4yOTQyNyA2MC45NDYxIDcuMjMwNDcgNjAuODA1NSA3LjE0NDUzIDYwLjY4MzFDNy4wNTg1OSA2MC41NTgxIDYuOTUwNTIgNjAuNDYwNCA2LjgyMDMxIDYwLjM5MDFDNi42OTI3MSA2MC4zMTcyIDYuNTQwMzYgNjAuMjgwOCA2LjM2MzI4IDYwLjI4MDhDNi4xNjI3NiA2MC4yODA4IDUuOTc1MjYgNjAuMzMwMiA1LjgwMDc4IDYwLjQyOTJDNS42Mjg5MSA2MC41MjU2IDUuNDg2OTggNjAuNjUzMiA1LjM3NSA2MC44MTJDNS4yNjU2MiA2MC45NjgzIDUuMjAzMTIgNjEuMTM4OCA1LjE4NzUgNjEuMzIzN0w0LjgwNDY5IDYxLjMxOThDNC44NDExNSA2MS4wMjgyIDQuOTA4ODUgNjAuNzc5NSA1LjAwNzgxIDYwLjU3MzdDNS4xMDkzOCA2MC4zNjU0IDUuMjM0MzggNjAuMTk2MSA1LjM4MjgxIDYwLjA2NTlDNS41MzM4NSA1OS45MzMxIDUuNzAxODIgNTkuODM2OCA1Ljg4NjcyIDU5Ljc3NjlDNi4wNzQyMiA1OS43MTQ0IDYuMjcyMTQgNTkuNjgzMSA2LjQ4MDQ3IDU5LjY4MzFDNi43NjQzMiA1OS42ODMxIDcuMDA5MTEgNTkuNzM2NSA3LjIxNDg0IDU5Ljg0MzNDNy40MjA1NyA1OS45NSA3LjU4OTg0IDYwLjA5MzMgNy43MjI2NiA2MC4yNzI5QzcuODU1NDcgNjAuNDUgNy45NTMxMiA2MC42NTA2IDguMDE1NjIgNjAuODc0NUM4LjA4MDczIDYxLjA5NTkgOC4xMTMyOCA2MS4zMjM3IDguMTEzMjggNjEuNTU4MUM4LjExMzI4IDYxLjgyNjMgOC4wNzU1MiA2Mi4wNzc2IDggNjIuMzEyQzcuOTI0NDggNjIuNTQ2NCA3LjgxMTIgNjIuNzUyMSA3LjY2MDE2IDYyLjkyOTJDNy41MTE3MiA2My4xMDYzIDcuMzI4MTIgNjMuMjQ0MyA3LjEwOTM4IDYzLjM0MzNDNi44OTA2MiA2My40NDIyIDYuNjM2NzIgNjMuNDkxNyA2LjM0NzY2IDYzLjQ5MTdDNi4wNDAzNiA2My40OTE3IDUuNzcyMTQgNjMuNDI5MiA1LjU0Mjk3IDYzLjMwNDJDNS4zMTM4IDYzLjE3NjYgNS4xMjM3IDYzLjAwNzMgNC45NzI2NiA2Mi43OTY0QzQuODIxNjEgNjIuNTg1NCA0LjcwODMzIDYyLjM1MTEgNC42MzI4MSA2Mi4wOTMzQzQuNTU3MjkgNjEuODM1NCA0LjUxOTUzIDYxLjU3MzcgNC41MTk1MyA2MS4zMDgxVjYwLjk2ODNDNC41MTk1MyA2MC41NjcyIDQuNTU5OSA2MC4xNzQgNC42NDA2MiA1OS43ODg2QzQuNzIxMzUgNTkuNDAzMiA0Ljg2MDY4IDU5LjA1NDIgNS4wNTg1OSA1OC43NDE3QzUuMjU5MTEgNTguNDI5MiA1LjUzNjQ2IDU4LjE4MDUgNS44OTA2MiA1Ny45OTU2QzYuMjQ0NzkgNTcuODEwNyA2LjY5NjYxIDU3LjcxODMgNy4yNDYwOSA1Ny43MTgzWk0xMi42NzUyIDYwLjExNjdWNjAuOTgzOUMxMi42NzUyIDYxLjQ1IDEyLjYzMzUgNjEuODQzMyAxMi41NTAyIDYyLjE2MzZDMTIuNDY2OCA2Mi40ODM5IDEyLjM0NyA2Mi43NDE3IDEyLjE5MDggNjIuOTM3QzEyLjAzNDUgNjMuMTMyMyAxMS44NDU3IDYzLjI3NDMgMTEuNjI0NCA2My4zNjI4QzExLjQwNTYgNjMuNDQ4NyAxMS4xNTgyIDYzLjQ5MTcgMTAuODgyMiA2My40OTE3QzEwLjY2MzUgNjMuNDkxNyAxMC40NjE2IDYzLjQ2NDQgMTAuMjc2NyA2My40MDk3QzEwLjA5MTggNjMuMzU1IDkuOTI1MTcgNjMuMjY3NyA5Ljc3NjczIDYzLjE0NzlDOS42MzA5IDYzLjAyNTYgOS41MDU5IDYyLjg2NjcgOS40MDE3MyA2Mi42NzE0QzkuMjk3NTcgNjIuNDc2MSA5LjIxODE0IDYyLjIzOTEgOS4xNjM0NSA2MS45NjA0QzkuMTA4NzcgNjEuNjgxOCA5LjA4MTQyIDYxLjM1NjMgOS4wODE0MiA2MC45ODM5VjYwLjExNjdDOS4wODE0MiA1OS42NTA2IDkuMTIzMDkgNTkuMjU5OSA5LjIwNjQyIDU4Ljk0NDhDOS4yOTIzNiA1OC42Mjk3IDkuNDEzNDUgNTguMzc3MSA5LjU2OTcgNTguMTg3QzkuNzI1OTUgNTcuOTk0MyA5LjkxMzQ1IDU3Ljg1NjMgMTAuMTMyMiA1Ny43NzI5QzEwLjM1MzYgNTcuNjg5NiAxMC42MDEgNTcuNjQ3OSAxMC44NzQ0IDU3LjY0NzlDMTEuMDk1NyA1Ny42NDc5IDExLjI5ODkgNTcuNjc1MyAxMS40ODM4IDU3LjczQzExLjY3MTMgNTcuNzgyMSAxMS44Mzc5IDU3Ljg2NjcgMTEuOTgzOCA1Ny45ODM5QzEyLjEyOTYgNTguMDk4NSAxMi4yNTMzIDU4LjI1MjEgMTIuMzU0OSA1OC40NDQ4QzEyLjQ1OSA1OC42MzQ5IDEyLjUzODUgNTguODY4IDEyLjU5MzEgNTkuMTQ0QzEyLjY0NzggNTkuNDIwMSAxMi42NzUyIDU5Ljc0NDMgMTIuNjc1MiA2MC4xMTY3Wk0xMS45NDg2IDYxLjEwMTFWNTkuOTk1NkMxMS45NDg2IDU5Ljc0MDQgMTEuOTMzIDU5LjUxNjQgMTEuOTAxNyA1OS4zMjM3QzExLjg3MzEgNTkuMTI4NCAxMS44MzAxIDU4Ljk2MTggMTEuNzcyOCA1OC44MjM3QzExLjcxNTUgNTguNjg1NyAxMS42NDI2IDU4LjU3MzcgMTEuNTU0MSA1OC40ODc4QzExLjQ2ODEgNTguNDAxOSAxMS4zNjc5IDU4LjMzOTQgMTEuMjUzMyA1OC4zMDAzQzExLjE0MTMgNTguMjU4NiAxMS4wMTUgNTguMjM3OCAxMC44NzQ0IDU4LjIzNzhDMTAuNzAyNSA1OC4yMzc4IDEwLjU1MDIgNTguMjcwMyAxMC40MTc0IDU4LjMzNTRDMTAuMjg0NSA1OC4zOTc5IDEwLjE3MjYgNTguNDk4MiAxMC4wODE0IDU4LjYzNjJDOS45OTI4OCA1OC43NzQzIDkuOTI1MTcgNTguOTU1MiA5Ljg3ODMgNTkuMTc5MkM5LjgzMTQyIDU5LjQwMzIgOS44MDc5OCA1OS42NzUzIDkuODA3OTggNTkuOTk1NlY2MS4xMDExQzkuODA3OTggNjEuMzU2MyA5LjgyMjMxIDYxLjU4MTUgOS44NTA5NSA2MS43NzY5QzkuODgyMiA2MS45NzIyIDkuOTI3NzggNjIuMTQxNCA5Ljk4NzY3IDYyLjI4NDdDMTAuMDQ3NiA2Mi40MjUzIDEwLjEyMDUgNjIuNTQxMiAxMC4yMDY0IDYyLjYzMjNDMTAuMjkyNCA2Mi43MjM1IDEwLjM5MTMgNjIuNzkxMiAxMC41MDMzIDYyLjgzNTRDMTAuNjE3OSA2Mi44NzcxIDEwLjc0NDIgNjIuODk3OSAxMC44ODIyIDYyLjg5NzlDMTEuMDU5MyA2Mi44OTc5IDExLjIxNDIgNjIuODY0MSAxMS4zNDcgNjIuNzk2NEMxMS40Nzk5IDYyLjcyODcgMTEuNTkwNSA2Mi42MjMyIDExLjY3OTEgNjIuNDhDMTEuNzcwMiA2Mi4zMzQxIDExLjgzNzkgNjIuMTQ3OSAxMS44ODIyIDYxLjkyMTRDMTEuOTI2NSA2MS42OTIyIDExLjk0ODYgNjEuNDE4OCAxMS45NDg2IDYxLjEwMTFaTTEzLjY3NDYgNTkuMTIwNlY1OC44MTk4QzEzLjY3NDYgNTguNjAzNyAxMy43MjE0IDU4LjQwNzEgMTMuODE1MiA1OC4yM0MxMy45MDg5IDU4LjA1MjkgMTQuMDQzMSA1Ny45MTEgMTQuMjE3NSA1Ny44MDQyQzE0LjM5MiA1Ny42OTc0IDE0LjU5OSA1Ny42NDQgMTQuODM4NiA1Ny42NDRDMTUuMDgzNCA1Ny42NDQgMTUuMjkxOCA1Ny42OTc0IDE1LjQ2MzYgNTcuODA0MkMxNS42MzgxIDU3LjkxMSAxNS43NzIyIDU4LjA1MjkgMTUuODY2IDU4LjIzQzE1Ljk1OTcgNTguNDA3MSAxNi4wMDY2IDU4LjYwMzcgMTYuMDA2NiA1OC44MTk4VjU5LjEyMDZDMTYuMDA2NiA1OS4zMzE1IDE1Ljk1OTcgNTkuNTI1NiAxNS44NjYgNTkuNzAyNkMxNS43NzQ4IDU5Ljg3OTcgMTUuNjQyIDYwLjAyMTYgMTUuNDY3NSA2MC4xMjg0QzE1LjI5NTcgNjAuMjM1MiAxNS4wODg2IDYwLjI4ODYgMTQuODQ2NCA2MC4yODg2QzE0LjYwNDMgNjAuMjg4NiAxNC4zOTQ2IDYwLjIzNTIgMTQuMjE3NSA2MC4xMjg0QzE0LjA0MzEgNjAuMDIxNiAxMy45MDg5IDU5Ljg3OTcgMTMuODE1MiA1OS43MDI2QzEzLjcyMTQgNTkuNTI1NiAxMy42NzQ2IDU5LjMzMTUgMTMuNjc0NiA1OS4xMjA2Wk0xNC4yMTc1IDU4LjgxOThWNTkuMTIwNkMxNC4yMTc1IDU5LjI0MDQgMTQuMjM5NyA1OS4zNTM3IDE0LjI4MzkgNTkuNDYwNEMxNC4zMzA4IDU5LjU2NzIgMTQuNDAxMSA1OS42NTQ1IDE0LjQ5NDkgNTkuNzIyMkMxNC41ODg2IDU5Ljc4NzMgMTQuNzA1OCA1OS44MTk4IDE0Ljg0NjQgNTkuODE5OEMxNC45ODcxIDU5LjgxOTggMTUuMTAyOSA1OS43ODczIDE1LjE5NDEgNTkuNzIyMkMxNS4yODUyIDU5LjY1NDUgMTUuMzUyOSA1OS41NjcyIDE1LjM5NzIgNTkuNDYwNEMxNS40NDE1IDU5LjM1MzcgMTUuNDYzNiA1OS4yNDA0IDE1LjQ2MzYgNTkuMTIwNlY1OC44MTk4QzE1LjQ2MzYgNTguNjk3NCAxNS40NDAyIDU4LjU4MjggMTUuMzkzMyA1OC40NzYxQzE1LjM0OSA1OC4zNjY3IDE1LjI4IDU4LjI3OTUgMTUuMTg2MyA1OC4yMTQ0QzE1LjA5NTEgNTguMTQ2NiAxNC45NzkzIDU4LjExMjggMTQuODM4NiA1OC4xMTI4QzE0LjcwMDYgNTguMTEyOCAxNC41ODQ3IDU4LjE0NjYgMTQuNDkxIDU4LjIxNDRDMTQuMzk5OCA1OC4yNzk1IDE0LjMzMDggNTguMzY2NyAxNC4yODM5IDU4LjQ3NjFDMTQuMjM5NyA1OC41ODI4IDE0LjIxNzUgNTguNjk3NCAxNC4yMTc1IDU4LjgxOThaTTE2LjQ0NDEgNjIuMzIzN1Y2Mi4wMTlDMTYuNDQ0MSA2MS44MDU1IDE2LjQ5MSA2MS42MTAyIDE2LjU4NDcgNjEuNDMzMUMxNi42Nzg1IDYxLjI1NiAxNi44MTI2IDYxLjExNDEgMTYuOTg3MSA2MS4wMDczQzE3LjE2MTUgNjAuOTAwNiAxNy4zNjg2IDYwLjg0NzIgMTcuNjA4MiA2MC44NDcyQzE3Ljg1MjkgNjAuODQ3MiAxOC4wNjEzIDYwLjkwMDYgMTguMjMzMiA2MS4wMDczQzE4LjQwNzYgNjEuMTE0MSAxOC41NDE4IDYxLjI1NiAxOC42MzU1IDYxLjQzMzFDMTguNzI5MyA2MS42MTAyIDE4Ljc3NjEgNjEuODA1NSAxOC43NzYxIDYyLjAxOVY2Mi4zMjM3QzE4Ljc3NjEgNjIuNTM3MyAxOC43MjkzIDYyLjczMjYgMTguNjM1NSA2Mi45MDk3QzE4LjU0NDQgNjMuMDg2OCAxOC40MTE1IDYzLjIyODcgMTguMjM3MSA2My4zMzU0QzE4LjA2NTIgNjMuNDQyMiAxNy44NTgyIDYzLjQ5NTYgMTcuNjE2IDYzLjQ5NTZDMTcuMzczOCA2My40OTU2IDE3LjE2NTQgNjMuNDQyMiAxNi45OTEgNjMuMzM1NEMxNi44MTY1IDYzLjIyODcgMTYuNjgxMSA2My4wODY4IDE2LjU4NDcgNjIuOTA5N0MxNi40OTEgNjIuNzMyNiAxNi40NDQxIDYyLjUzNzMgMTYuNDQ0MSA2Mi4zMjM3Wk0xNi45ODcxIDYyLjAxOVY2Mi4zMjM3QzE2Ljk4NzEgNjIuNDQzNSAxNy4wMDkyIDYyLjU1ODEgMTcuMDUzNSA2Mi42Njc1QzE3LjEwMDMgNjIuNzc0MyAxNy4xNzA3IDYyLjg2MTUgMTcuMjY0NCA2Mi45MjkyQzE3LjM1ODIgNjIuOTk0MyAxNy40NzUzIDYzLjAyNjkgMTcuNjE2IDYzLjAyNjlDMTcuNzU2NiA2My4wMjY5IDE3Ljg3MjUgNjIuOTk0MyAxNy45NjM2IDYyLjkyOTJDMTguMDU3NCA2Mi44NjE1IDE4LjEyNjQgNjIuNzc0MyAxOC4xNzA3IDYyLjY2NzVDMTguMjE0OSA2Mi41NjA3IDE4LjIzNzEgNjIuNDQ2MSAxOC4yMzcxIDYyLjMyMzdWNjIuMDE5QzE4LjIzNzEgNjEuODk2NiAxOC4yMTM2IDYxLjc4MjEgMTguMTY2OCA2MS42NzUzQzE4LjEyMjUgNjEuNTY4NSAxOC4wNTM1IDYxLjQ4MjYgMTcuOTU5NyA2MS40MTc1QzE3Ljg2ODYgNjEuMzQ5OCAxNy43NTE0IDYxLjMxNTkgMTcuNjA4MiA2MS4zMTU5QzE3LjQ3MDEgNjEuMzE1OSAxNy4zNTQzIDYxLjM0OTggMTcuMjYwNSA2MS40MTc1QzE3LjE2OTQgNjEuNDgyNiAxNy4xMDAzIDYxLjU2ODUgMTcuMDUzNSA2MS42NzUzQzE3LjAwOTIgNjEuNzgyMSAxNi45ODcxIDYxLjg5NjYgMTYuOTg3MSA2Mi4wMTlaTTE3Ljc4NzggNTguNTM0N0wxNS4wMTA1IDYyLjk4TDE0LjYwNDMgNjIuNzIyMkwxNy4zODE2IDU4LjI3NjlMMTcuNzg3OCA1OC41MzQ3WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNOC4zMTY0MSA4OS43MDYzVjkwLjNINC4yMDcwM1Y4OS44NzQzTDYuNzUzOTEgODUuOTMyOUg3LjM0Mzc1TDYuNzEwOTQgODcuMDczNUw1LjAyNzM0IDg5LjcwNjNIOC4zMTY0MVpNNy41MjM0NCA4NS45MzI5VjkxLjYyMDRINi44MDA3OFY4NS45MzI5SDcuNTIzNDRaTTEyLjY3NTIgODguMzIzNVY4OS4xOTA3QzEyLjY3NTIgODkuNjU2OCAxMi42MzM1IDkwLjA1IDEyLjU1MDIgOTAuMzcwNEMxMi40NjY4IDkwLjY5MDcgMTIuMzQ3IDkwLjk0ODUgMTIuMTkwOCA5MS4xNDM4QzEyLjAzNDUgOTEuMzM5MSAxMS44NDU3IDkxLjQ4MSAxMS42MjQ0IDkxLjU2OTZDMTEuNDA1NiA5MS42NTU1IDExLjE1ODIgOTEuNjk4NSAxMC44ODIyIDkxLjY5ODVDMTAuNjYzNSA5MS42OTg1IDEwLjQ2MTYgOTEuNjcxMSAxMC4yNzY3IDkxLjYxNjVDMTAuMDkxOCA5MS41NjE4IDkuOTI1MTcgOTEuNDc0NSA5Ljc3NjczIDkxLjM1NDdDOS42MzA5IDkxLjIzMjMgOS41MDU5IDkxLjA3MzUgOS40MDE3MyA5MC44NzgyQzkuMjk3NTcgOTAuNjgyOSA5LjIxODE0IDkwLjQ0NTkgOS4xNjM0NSA5MC4xNjcyQzkuMTA4NzcgODkuODg4NiA5LjA4MTQyIDg5LjU2MzEgOS4wODE0MiA4OS4xOTA3Vjg4LjMyMzVDOS4wODE0MiA4Ny44NTczIDkuMTIzMDkgODcuNDY2NyA5LjIwNjQyIDg3LjE1MTZDOS4yOTIzNiA4Ni44MzY1IDkuNDEzNDUgODYuNTgzOSA5LjU2OTcgODYuMzkzOEM5LjcyNTk1IDg2LjIwMTEgOS45MTM0NSA4Ni4wNjMxIDEwLjEzMjIgODUuOTc5N0MxMC4zNTM2IDg1Ljg5NjQgMTAuNjAxIDg1Ljg1NDcgMTAuODc0NCA4NS44NTQ3QzExLjA5NTcgODUuODU0NyAxMS4yOTg5IDg1Ljg4MjEgMTEuNDgzOCA4NS45MzY4QzExLjY3MTMgODUuOTg4OSAxMS44Mzc5IDg2LjA3MzUgMTEuOTgzOCA4Ni4xOTA3QzEyLjEyOTYgODYuMzA1MyAxMi4yNTMzIDg2LjQ1ODkgMTIuMzU0OSA4Ni42NTE2QzEyLjQ1OSA4Ni44NDE3IDEyLjUzODUgODcuMDc0OCAxMi41OTMxIDg3LjM1MDhDMTIuNjQ3OCA4Ny42MjY5IDEyLjY3NTIgODcuOTUxMSAxMi42NzUyIDg4LjMyMzVaTTExLjk0ODYgODkuMzA3OVY4OC4yMDI0QzExLjk0ODYgODcuOTQ3MiAxMS45MzMgODcuNzIzMiAxMS45MDE3IDg3LjUzMDVDMTEuODczMSA4Ny4zMzUyIDExLjgzMDEgODcuMTY4NSAxMS43NzI4IDg3LjAzMDVDMTEuNzE1NSA4Ni44OTI1IDExLjY0MjYgODYuNzgwNSAxMS41NTQxIDg2LjY5NDZDMTEuNDY4MSA4Ni42MDg2IDExLjM2NzkgODYuNTQ2MSAxMS4yNTMzIDg2LjUwNzFDMTEuMTQxMyA4Ni40NjU0IDExLjAxNSA4Ni40NDQ2IDEwLjg3NDQgODYuNDQ0NkMxMC43MDI1IDg2LjQ0NDYgMTAuNTUwMiA4Ni40NzcxIDEwLjQxNzQgODYuNTQyMkMxMC4yODQ1IDg2LjYwNDcgMTAuMTcyNiA4Ni43MDUgMTAuMDgxNCA4Ni44NDNDOS45OTI4OCA4Ni45ODEgOS45MjUxNyA4Ny4xNjIgOS44NzgzIDg3LjM4NkM5LjgzMTQyIDg3LjYwOTkgOS44MDc5OCA4Ny44ODIxIDkuODA3OTggODguMjAyNFY4OS4zMDc5QzkuODA3OTggODkuNTYzMSA5LjgyMjMxIDg5Ljc4ODMgOS44NTA5NSA4OS45ODM2QzkuODgyMiA5MC4xNzkgOS45Mjc3OCA5MC4zNDgyIDkuOTg3NjcgOTAuNDkxNUMxMC4wNDc2IDkwLjYzMjEgMTAuMTIwNSA5MC43NDggMTAuMjA2NCA5MC44MzkxQzEwLjI5MjQgOTAuOTMwMyAxMC4zOTEzIDkwLjk5OCAxMC41MDMzIDkxLjA0MjJDMTAuNjE3OSA5MS4wODM5IDEwLjc0NDIgOTEuMTA0NyAxMC44ODIyIDkxLjEwNDdDMTEuMDU5MyA5MS4xMDQ3IDExLjIxNDIgOTEuMDcwOSAxMS4zNDcgOTEuMDAzMkMxMS40Nzk5IDkwLjkzNTUgMTEuNTkwNSA5MC44MyAxMS42NzkxIDkwLjY4NjhDMTEuNzcwMiA5MC41NDA5IDExLjgzNzkgOTAuMzU0NyAxMS44ODIyIDkwLjEyODJDMTEuOTI2NSA4OS44OTkgMTEuOTQ4NiA4OS42MjU2IDExLjk0ODYgODkuMzA3OVpNMTMuNjc0NiA4Ny4zMjc0Vjg3LjAyNjZDMTMuNjc0NiA4Ni44MTA1IDEzLjcyMTQgODYuNjEzOSAxMy44MTUyIDg2LjQzNjhDMTMuOTA4OSA4Ni4yNTk3IDE0LjA0MzEgODYuMTE3OCAxNC4yMTc1IDg2LjAxMUMxNC4zOTIgODUuOTA0MiAxNC41OTkgODUuODUwOCAxNC44Mzg2IDg1Ljg1MDhDMTUuMDgzNCA4NS44NTA4IDE1LjI5MTggODUuOTA0MiAxNS40NjM2IDg2LjAxMUMxNS42MzgxIDg2LjExNzggMTUuNzcyMiA4Ni4yNTk3IDE1Ljg2NiA4Ni40MzY4QzE1Ljk1OTcgODYuNjEzOSAxNi4wMDY2IDg2LjgxMDUgMTYuMDA2NiA4Ny4wMjY2Vjg3LjMyNzRDMTYuMDA2NiA4Ny41MzgzIDE1Ljk1OTcgODcuNzMyMyAxNS44NjYgODcuOTA5NEMxNS43NzQ4IDg4LjA4NjUgMTUuNjQyIDg4LjIyODQgMTUuNDY3NSA4OC4zMzUyQzE1LjI5NTcgODguNDQyIDE1LjA4ODYgODguNDk1NCAxNC44NDY0IDg4LjQ5NTRDMTQuNjA0MyA4OC40OTU0IDE0LjM5NDYgODguNDQyIDE0LjIxNzUgODguMzM1MkMxNC4wNDMxIDg4LjIyODQgMTMuOTA4OSA4OC4wODY1IDEzLjgxNTIgODcuOTA5NEMxMy43MjE0IDg3LjczMjMgMTMuNjc0NiA4Ny41MzgzIDEzLjY3NDYgODcuMzI3NFpNMTQuMjE3NSA4Ny4wMjY2Vjg3LjMyNzRDMTQuMjE3NSA4Ny40NDcyIDE0LjIzOTcgODcuNTYwNSAxNC4yODM5IDg3LjY2NzJDMTQuMzMwOCA4Ny43NzQgMTQuNDAxMSA4Ny44NjEyIDE0LjQ5NDkgODcuOTI5QzE0LjU4ODYgODcuOTk0MSAxNC43MDU4IDg4LjAyNjYgMTQuODQ2NCA4OC4wMjY2QzE0Ljk4NzEgODguMDI2NiAxNS4xMDI5IDg3Ljk5NDEgMTUuMTk0MSA4Ny45MjlDMTUuMjg1MiA4Ny44NjEyIDE1LjM1MjkgODcuNzc0IDE1LjM5NzIgODcuNjY3MkMxNS40NDE1IDg3LjU2MDUgMTUuNDYzNiA4Ny40NDcyIDE1LjQ2MzYgODcuMzI3NFY4Ny4wMjY2QzE1LjQ2MzYgODYuOTA0MiAxNS40NDAyIDg2Ljc4OTYgMTUuMzkzMyA4Ni42ODI5QzE1LjM0OSA4Ni41NzM1IDE1LjI4IDg2LjQ4NjIgMTUuMTg2MyA4Ni40MjExQzE1LjA5NTEgODYuMzUzNCAxNC45NzkzIDg2LjMxOTYgMTQuODM4NiA4Ni4zMTk2QzE0LjcwMDYgODYuMzE5NiAxNC41ODQ3IDg2LjM1MzQgMTQuNDkxIDg2LjQyMTFDMTQuMzk5OCA4Ni40ODYyIDE0LjMzMDggODYuNTczNSAxNC4yODM5IDg2LjY4MjlDMTQuMjM5NyA4Ni43ODk2IDE0LjIxNzUgODYuOTA0MiAxNC4yMTc1IDg3LjAyNjZaTTE2LjQ0NDEgOTAuNTMwNVY5MC4yMjU4QzE2LjQ0NDEgOTAuMDEyMyAxNi40OTEgODkuODE3IDE2LjU4NDcgODkuNjM5OUMxNi42Nzg1IDg5LjQ2MjggMTYuODEyNiA4OS4zMjA5IDE2Ljk4NzEgODkuMjE0MUMxNy4xNjE1IDg5LjEwNzMgMTcuMzY4NiA4OS4wNTQgMTcuNjA4MiA4OS4wNTRDMTcuODUyOSA4OS4wNTQgMTguMDYxMyA4OS4xMDczIDE4LjIzMzIgODkuMjE0MUMxOC40MDc2IDg5LjMyMDkgMTguNTQxOCA4OS40NjI4IDE4LjYzNTUgODkuNjM5OUMxOC43MjkzIDg5LjgxNyAxOC43NzYxIDkwLjAxMjMgMTguNzc2MSA5MC4yMjU4VjkwLjUzMDVDMTguNzc2MSA5MC43NDQxIDE4LjcyOTMgOTAuOTM5NCAxOC42MzU1IDkxLjExNjVDMTguNTQ0NCA5MS4yOTM1IDE4LjQxMTUgOTEuNDM1NSAxOC4yMzcxIDkxLjU0MjJDMTguMDY1MiA5MS42NDkgMTcuODU4MiA5MS43MDI0IDE3LjYxNiA5MS43MDI0QzE3LjM3MzggOTEuNzAyNCAxNy4xNjU0IDkxLjY0OSAxNi45OTEgOTEuNTQyMkMxNi44MTY1IDkxLjQzNTUgMTYuNjgxMSA5MS4yOTM1IDE2LjU4NDcgOTEuMTE2NUMxNi40OTEgOTAuOTM5NCAxNi40NDQxIDkwLjc0NDEgMTYuNDQ0MSA5MC41MzA1Wk0xNi45ODcxIDkwLjIyNThWOTAuNTMwNUMxNi45ODcxIDkwLjY1MDMgMTcuMDA5MiA5MC43NjQ5IDE3LjA1MzUgOTAuODc0M0MxNy4xMDAzIDkwLjk4MSAxNy4xNzA3IDkxLjA2ODMgMTcuMjY0NCA5MS4xMzZDMTcuMzU4MiA5MS4yMDExIDE3LjQ3NTMgOTEuMjMzNiAxNy42MTYgOTEuMjMzNkMxNy43NTY2IDkxLjIzMzYgMTcuODcyNSA5MS4yMDExIDE3Ljk2MzYgOTEuMTM2QzE4LjA1NzQgOTEuMDY4MyAxOC4xMjY0IDkwLjk4MSAxOC4xNzA3IDkwLjg3NDNDMTguMjE0OSA5MC43Njc1IDE4LjIzNzEgOTAuNjUyOSAxOC4yMzcxIDkwLjUzMDVWOTAuMjI1OEMxOC4yMzcxIDkwLjEwMzQgMTguMjEzNiA4OS45ODg5IDE4LjE2NjggODkuODgyMUMxOC4xMjI1IDg5Ljc3NTMgMTguMDUzNSA4OS42ODk0IDE3Ljk1OTcgODkuNjI0M0MxNy44Njg2IDg5LjU1NjYgMTcuNzUxNCA4OS41MjI3IDE3LjYwODIgODkuNTIyN0MxNy40NzAxIDg5LjUyMjcgMTcuMzU0MyA4OS41NTY2IDE3LjI2MDUgODkuNjI0M0MxNy4xNjk0IDg5LjY4OTQgMTcuMTAwMyA4OS43NzUzIDE3LjA1MzUgODkuODgyMUMxNy4wMDkyIDg5Ljk4ODkgMTYuOTg3MSA5MC4xMDM0IDE2Ljk4NzEgOTAuMjI1OFpNMTcuNzg3OCA4Ni43NDE1TDE1LjAxMDUgOTEuMTg2OEwxNC42MDQzIDkwLjkyOUwxNy4zODE2IDg2LjQ4MzZMMTcuNzg3OCA4Ni43NDE1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNOC4xOTkyMiAxMTkuMjMzVjExOS44MjdINC40NzY1NlYxMTkuMzA4TDYuMzM5ODQgMTE3LjIzM0M2LjU2OTAxIDExNi45NzggNi43NDYwOSAxMTYuNzYyIDYuODcxMDkgMTE2LjU4NUM2Ljk5ODcgMTE2LjQwNSA3LjA4NzI0IDExNi4yNDUgNy4xMzY3MiAxMTYuMTA0QzcuMTg4OCAxMTUuOTYxIDcuMjE0ODQgMTE1LjgxNSA3LjIxNDg0IDExNS42NjdDNy4yMTQ4NCAxMTUuNDc5IDcuMTc1NzggMTE1LjMxIDcuMDk3NjYgMTE1LjE1OUM3LjAyMjE0IDExNS4wMDYgNi45MTAxNiAxMTQuODgzIDYuNzYxNzIgMTE0Ljc5MkM2LjYxMzI4IDExNC43MDEgNi40MzM1OSAxMTQuNjU1IDYuMjIyNjYgMTE0LjY1NUM1Ljk3MDA1IDExNC42NTUgNS43NTkxMSAxMTQuNzA1IDUuNTg5ODQgMTE0LjgwNEM1LjQyMzE4IDExNC45IDUuMjk4MTggMTE1LjAzNSA1LjIxNDg0IDExNS4yMUM1LjEzMTUxIDExNS4zODQgNS4wODk4NCAxMTUuNTg1IDUuMDg5ODQgMTE1LjgxMkg0LjM2NzE5QzQuMzY3MTkgMTE1LjQ5MSA0LjQzNzUgMTE1LjE5OCA0LjU3ODEyIDExNC45MzNDNC43MTg3NSAxMTQuNjY3IDQuOTI3MDggMTE0LjQ1NiA1LjIwMzEyIDExNC4zQzUuNDc5MTcgMTE0LjE0MSA1LjgxOTAxIDExNC4wNjIgNi4yMjI2NiAxMTQuMDYyQzYuNTgyMDMgMTE0LjA2MiA2Ljg4OTMyIDExNC4xMjUgNy4xNDQ1MyAxMTQuMjUzQzcuMzk5NzQgMTE0LjM3OCA3LjU5NTA1IDExNC41NTUgNy43MzA0NyAxMTQuNzg0QzcuODY4NDkgMTE1LjAxMSA3LjkzNzUgMTE1LjI3NiA3LjkzNzUgMTE1LjU4MUM3LjkzNzUgMTE1Ljc0OCA3LjkwODg1IDExNS45MTcgNy44NTE1NiAxMTYuMDg5QzcuNzk2ODggMTE2LjI1OCA3LjcyMDA1IDExNi40MjcgNy42MjEwOSAxMTYuNTk3QzcuNTI0NzQgMTE2Ljc2NiA3LjQxMTQ2IDExNi45MzMgNy4yODEyNSAxMTcuMDk3QzcuMTUzNjUgMTE3LjI2MSA3LjAxNjkzIDExNy40MjIgNi44NzEwOSAxMTcuNTgxTDUuMzQ3NjYgMTE5LjIzM0g4LjE5OTIyWk0xMi42NzUyIDExNi41M1YxMTcuMzk3QzEyLjY3NTIgMTE3Ljg2NCAxMi42MzM1IDExOC4yNTcgMTIuNTUwMiAxMTguNTc3QzEyLjQ2NjggMTE4Ljg5NyAxMi4zNDcgMTE5LjE1NSAxMi4xOTA4IDExOS4zNTFDMTIuMDM0NSAxMTkuNTQ2IDExLjg0NTcgMTE5LjY4OCAxMS42MjQ0IDExOS43NzZDMTEuNDA1NiAxMTkuODYyIDExLjE1ODIgMTE5LjkwNSAxMC44ODIyIDExOS45MDVDMTAuNjYzNSAxMTkuOTA1IDEwLjQ2MTYgMTE5Ljg3OCAxMC4yNzY3IDExOS44MjNDMTAuMDkxOCAxMTkuNzY5IDkuOTI1MTcgMTE5LjY4MSA5Ljc3NjczIDExOS41NjJDOS42MzA5IDExOS40MzkgOS41MDU5IDExOS4yOCA5LjQwMTczIDExOS4wODVDOS4yOTc1NyAxMTguODkgOS4yMTgxNCAxMTguNjUzIDkuMTYzNDUgMTE4LjM3NEM5LjEwODc3IDExOC4wOTUgOS4wODE0MiAxMTcuNzcgOS4wODE0MiAxMTcuMzk3VjExNi41M0M5LjA4MTQyIDExNi4wNjQgOS4xMjMwOSAxMTUuNjc0IDkuMjA2NDIgMTE1LjM1OEM5LjI5MjM2IDExNS4wNDMgOS40MTM0NSAxMTQuNzkxIDkuNTY5NyAxMTQuNjAxQzkuNzI1OTUgMTE0LjQwOCA5LjkxMzQ1IDExNC4yNyAxMC4xMzIyIDExNC4xODdDMTAuMzUzNiAxMTQuMTAzIDEwLjYwMSAxMTQuMDYyIDEwLjg3NDQgMTE0LjA2MkMxMS4wOTU3IDExNC4wNjIgMTEuMjk4OSAxMTQuMDg5IDExLjQ4MzggMTE0LjE0NEMxMS42NzEzIDExNC4xOTYgMTEuODM3OSAxMTQuMjggMTEuOTgzOCAxMTQuMzk3QzEyLjEyOTYgMTE0LjUxMiAxMi4yNTMzIDExNC42NjYgMTIuMzU0OSAxMTQuODU4QzEyLjQ1OSAxMTUuMDQ5IDEyLjUzODUgMTE1LjI4MiAxMi41OTMxIDExNS41NThDMTIuNjQ3OCAxMTUuODM0IDEyLjY3NTIgMTE2LjE1OCAxMi42NzUyIDExNi41M1pNMTEuOTQ4NiAxMTcuNTE1VjExNi40MDlDMTEuOTQ4NiAxMTYuMTU0IDExLjkzMyAxMTUuOTMgMTEuOTAxNyAxMTUuNzM3QzExLjg3MzEgMTE1LjU0MiAxMS44MzAxIDExNS4zNzUgMTEuNzcyOCAxMTUuMjM3QzExLjcxNTUgMTE1LjA5OSAxMS42NDI2IDExNC45ODcgMTEuNTU0MSAxMTQuOTAxQzExLjQ2ODEgMTE0LjgxNSAxMS4zNjc5IDExNC43NTMgMTEuMjUzMyAxMTQuNzE0QzExLjE0MTMgMTE0LjY3MiAxMS4wMTUgMTE0LjY1MSAxMC44NzQ0IDExNC42NTFDMTAuNzAyNSAxMTQuNjUxIDEwLjU1MDIgMTE0LjY4NCAxMC40MTc0IDExNC43NDlDMTAuMjg0NSAxMTQuODEyIDEwLjE3MjYgMTE0LjkxMiAxMC4wODE0IDExNS4wNUM5Ljk5Mjg4IDExNS4xODggOS45MjUxNyAxMTUuMzY5IDkuODc4MyAxMTUuNTkzQzkuODMxNDIgMTE1LjgxNyA5LjgwNzk4IDExNi4wODkgOS44MDc5OCAxMTYuNDA5VjExNy41MTVDOS44MDc5OCAxMTcuNzcgOS44MjIzMSAxMTcuOTk1IDkuODUwOTUgMTE4LjE5QzkuODgyMiAxMTguMzg2IDkuOTI3NzggMTE4LjU1NSA5Ljk4NzY3IDExOC42OThDMTAuMDQ3NiAxMTguODM5IDEwLjEyMDUgMTE4Ljk1NSAxMC4yMDY0IDExOS4wNDZDMTAuMjkyNCAxMTkuMTM3IDEwLjM5MTMgMTE5LjIwNSAxMC41MDMzIDExOS4yNDlDMTAuNjE3OSAxMTkuMjkxIDEwLjc0NDIgMTE5LjMxMiAxMC44ODIyIDExOS4zMTJDMTEuMDU5MyAxMTkuMzEyIDExLjIxNDIgMTE5LjI3OCAxMS4zNDcgMTE5LjIxQzExLjQ3OTkgMTE5LjE0MiAxMS41OTA1IDExOS4wMzcgMTEuNjc5MSAxMTguODk0QzExLjc3MDIgMTE4Ljc0OCAxMS44Mzc5IDExOC41NjIgMTEuODgyMiAxMTguMzM1QzExLjkyNjUgMTE4LjEwNiAxMS45NDg2IDExNy44MzIgMTEuOTQ4NiAxMTcuNTE1Wk0xMy42NzQ2IDExNS41MzRWMTE1LjIzM0MxMy42NzQ2IDExNS4wMTcgMTMuNzIxNCAxMTQuODIxIDEzLjgxNTIgMTE0LjY0NEMxMy45MDg5IDExNC40NjYgMTQuMDQzMSAxMTQuMzI1IDE0LjIxNzUgMTE0LjIxOEMxNC4zOTIgMTE0LjExMSAxNC41OTkgMTE0LjA1OCAxNC44Mzg2IDExNC4wNThDMTUuMDgzNCAxMTQuMDU4IDE1LjI5MTggMTE0LjExMSAxNS40NjM2IDExNC4yMThDMTUuNjM4MSAxMTQuMzI1IDE1Ljc3MjIgMTE0LjQ2NiAxNS44NjYgMTE0LjY0NEMxNS45NTk3IDExNC44MjEgMTYuMDA2NiAxMTUuMDE3IDE2LjAwNjYgMTE1LjIzM1YxMTUuNTM0QzE2LjAwNjYgMTE1Ljc0NSAxNS45NTk3IDExNS45MzkgMTUuODY2IDExNi4xMTZDMTUuNzc0OCAxMTYuMjkzIDE1LjY0MiAxMTYuNDM1IDE1LjQ2NzUgMTE2LjU0MkMxNS4yOTU3IDExNi42NDkgMTUuMDg4NiAxMTYuNzAyIDE0Ljg0NjQgMTE2LjcwMkMxNC42MDQzIDExNi43MDIgMTQuMzk0NiAxMTYuNjQ5IDE0LjIxNzUgMTE2LjU0MkMxNC4wNDMxIDExNi40MzUgMTMuOTA4OSAxMTYuMjkzIDEzLjgxNTIgMTE2LjExNkMxMy43MjE0IDExNS45MzkgMTMuNjc0NiAxMTUuNzQ1IDEzLjY3NDYgMTE1LjUzNFpNMTQuMjE3NSAxMTUuMjMzVjExNS41MzRDMTQuMjE3NSAxMTUuNjU0IDE0LjIzOTcgMTE1Ljc2NyAxNC4yODM5IDExNS44NzRDMTQuMzMwOCAxMTUuOTgxIDE0LjQwMTEgMTE2LjA2OCAxNC40OTQ5IDExNi4xMzZDMTQuNTg4NiAxMTYuMjAxIDE0LjcwNTggMTE2LjIzMyAxNC44NDY0IDExNi4yMzNDMTQuOTg3MSAxMTYuMjMzIDE1LjEwMjkgMTE2LjIwMSAxNS4xOTQxIDExNi4xMzZDMTUuMjg1MiAxMTYuMDY4IDE1LjM1MjkgMTE1Ljk4MSAxNS4zOTcyIDExNS44NzRDMTUuNDQxNSAxMTUuNzY3IDE1LjQ2MzYgMTE1LjY1NCAxNS40NjM2IDExNS41MzRWMTE1LjIzM0MxNS40NjM2IDExNS4xMTEgMTUuNDQwMiAxMTQuOTk2IDE1LjM5MzMgMTE0Ljg5QzE1LjM0OSAxMTQuNzggMTUuMjggMTE0LjY5MyAxNS4xODYzIDExNC42MjhDMTUuMDk1MSAxMTQuNTYgMTQuOTc5MyAxMTQuNTI2IDE0LjgzODYgMTE0LjUyNkMxNC43MDA2IDExNC41MjYgMTQuNTg0NyAxMTQuNTYgMTQuNDkxIDExNC42MjhDMTQuMzk5OCAxMTQuNjkzIDE0LjMzMDggMTE0Ljc4IDE0LjI4MzkgMTE0Ljg5QzE0LjIzOTcgMTE0Ljk5NiAxNC4yMTc1IDExNS4xMTEgMTQuMjE3NSAxMTUuMjMzWk0xNi40NDQxIDExOC43MzdWMTE4LjQzM0MxNi40NDQxIDExOC4yMTkgMTYuNDkxIDExOC4wMjQgMTYuNTg0NyAxMTcuODQ3QzE2LjY3ODUgMTE3LjY3IDE2LjgxMjYgMTE3LjUyOCAxNi45ODcxIDExNy40MjFDMTcuMTYxNSAxMTcuMzE0IDE3LjM2ODYgMTE3LjI2MSAxNy42MDgyIDExNy4yNjFDMTcuODUyOSAxMTcuMjYxIDE4LjA2MTMgMTE3LjMxNCAxOC4yMzMyIDExNy40MjFDMTguNDA3NiAxMTcuNTI4IDE4LjU0MTggMTE3LjY3IDE4LjYzNTUgMTE3Ljg0N0MxOC43MjkzIDExOC4wMjQgMTguNzc2MSAxMTguMjE5IDE4Ljc3NjEgMTE4LjQzM1YxMTguNzM3QzE4Ljc3NjEgMTE4Ljk1MSAxOC43MjkzIDExOS4xNDYgMTguNjM1NSAxMTkuMzIzQzE4LjU0NDQgMTE5LjUgMTguNDExNSAxMTkuNjQyIDE4LjIzNzEgMTE5Ljc0OUMxOC4wNjUyIDExOS44NTYgMTcuODU4MiAxMTkuOTA5IDE3LjYxNiAxMTkuOTA5QzE3LjM3MzggMTE5LjkwOSAxNy4xNjU0IDExOS44NTYgMTYuOTkxIDExOS43NDlDMTYuODE2NSAxMTkuNjQyIDE2LjY4MTEgMTE5LjUgMTYuNTg0NyAxMTkuMzIzQzE2LjQ5MSAxMTkuMTQ2IDE2LjQ0NDEgMTE4Ljk1MSAxNi40NDQxIDExOC43MzdaTTE2Ljk4NzEgMTE4LjQzM1YxMTguNzM3QzE2Ljk4NzEgMTE4Ljg1NyAxNy4wMDkyIDExOC45NzIgMTcuMDUzNSAxMTkuMDgxQzE3LjEwMDMgMTE5LjE4OCAxNy4xNzA3IDExOS4yNzUgMTcuMjY0NCAxMTkuMzQzQzE3LjM1ODIgMTE5LjQwOCAxNy40NzUzIDExOS40NCAxNy42MTYgMTE5LjQ0QzE3Ljc1NjYgMTE5LjQ0IDE3Ljg3MjUgMTE5LjQwOCAxNy45NjM2IDExOS4zNDNDMTguMDU3NCAxMTkuMjc1IDE4LjEyNjQgMTE5LjE4OCAxOC4xNzA3IDExOS4wODFDMTguMjE0OSAxMTguOTc0IDE4LjIzNzEgMTE4Ljg2IDE4LjIzNzEgMTE4LjczN1YxMTguNDMzQzE4LjIzNzEgMTE4LjMxIDE4LjIxMzYgMTE4LjE5NiAxOC4xNjY4IDExOC4wODlDMTguMTIyNSAxMTcuOTgyIDE4LjA1MzUgMTE3Ljg5NiAxNy45NTk3IDExNy44MzFDMTcuODY4NiAxMTcuNzYzIDE3Ljc1MTQgMTE3LjcyOSAxNy42MDgyIDExNy43MjlDMTcuNDcwMSAxMTcuNzI5IDE3LjM1NDMgMTE3Ljc2MyAxNy4yNjA1IDExNy44MzFDMTcuMTY5NCAxMTcuODk2IDE3LjEwMDMgMTE3Ljk4MiAxNy4wNTM1IDExOC4wODlDMTcuMDA5MiAxMTguMTk2IDE2Ljk4NzEgMTE4LjMxIDE2Ljk4NzEgMTE4LjQzM1pNMTcuNzg3OCAxMTQuOTQ4TDE1LjAxMDUgMTE5LjM5NEwxNC42MDQzIDExOS4xMzZMMTcuMzgxNiAxMTQuNjlMMTcuNzg3OCAxMTQuOTQ4WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNMTMuMDQzIDE0NC43MzdWMTQ1LjYwNEMxMy4wNDMgMTQ2LjA3IDEzLjAwMTMgMTQ2LjQ2NCAxMi45MTggMTQ2Ljc4NEMxMi44MzQ2IDE0Ny4xMDQgMTIuNzE0OCAxNDcuMzYyIDEyLjU1ODYgMTQ3LjU1N0MxMi40MDIzIDE0Ny43NTMgMTIuMjEzNSAxNDcuODk1IDExLjk5MjIgMTQ3Ljk4M0MxMS43NzM0IDE0OC4wNjkgMTEuNTI2IDE0OC4xMTIgMTEuMjUgMTQ4LjExMkMxMS4wMzEyIDE0OC4xMTIgMTAuODI5NCAxNDguMDg1IDEwLjY0NDUgMTQ4LjAzQzEwLjQ1OTYgMTQ3Ljk3NSAxMC4yOTMgMTQ3Ljg4OCAxMC4xNDQ1IDE0Ny43NjhDOS45OTg3IDE0Ny42NDYgOS44NzM3IDE0Ny40ODcgOS43Njk1MyAxNDcuMjkyQzkuNjY1MzYgMTQ3LjA5NiA5LjU4NTk0IDE0Ni44NTkgOS41MzEyNSAxNDYuNTgxQzkuNDc2NTYgMTQ2LjMwMiA5LjQ0OTIyIDE0NS45NzcgOS40NDkyMiAxNDUuNjA0VjE0NC43MzdDOS40NDkyMiAxNDQuMjcxIDkuNDkwODkgMTQzLjg4IDkuNTc0MjIgMTQzLjU2NUM5LjY2MDE2IDE0My4yNSA5Ljc4MTI1IDE0Mi45OTcgOS45Mzc1IDE0Mi44MDdDMTAuMDkzOCAxNDIuNjE1IDEwLjI4MTIgMTQyLjQ3NyAxMC41IDE0Mi4zOTNDMTAuNzIxNCAxNDIuMzEgMTAuOTY4OCAxNDIuMjY4IDExLjI0MjIgMTQyLjI2OEMxMS40NjM1IDE0Mi4yNjggMTEuNjY2NyAxNDIuMjk2IDExLjg1MTYgMTQyLjM1QzEyLjAzOTEgMTQyLjQwMiAxMi4yMDU3IDE0Mi40ODcgMTIuMzUxNiAxNDIuNjA0QzEyLjQ5NzQgMTQyLjcxOSAxMi42MjExIDE0Mi44NzIgMTIuNzIyNyAxNDMuMDY1QzEyLjgyNjggMTQzLjI1NSAxMi45MDYyIDE0My40ODggMTIuOTYwOSAxNDMuNzY0QzEzLjAxNTYgMTQ0LjA0IDEzLjA0MyAxNDQuMzY1IDEzLjA0MyAxNDQuNzM3Wk0xMi4zMTY0IDE0NS43MjFWMTQ0LjYxNkMxMi4zMTY0IDE0NC4zNjEgMTIuMzAwOCAxNDQuMTM3IDEyLjI2OTUgMTQzLjk0NEMxMi4yNDA5IDE0My43NDkgMTIuMTk3OSAxNDMuNTgyIDEyLjE0MDYgMTQzLjQ0NEMxMi4wODMzIDE0My4zMDYgMTIuMDEwNCAxNDMuMTk0IDExLjkyMTkgMTQzLjEwOEMxMS44MzU5IDE0My4wMjIgMTEuNzM1NyAxNDIuOTYgMTEuNjIxMSAxNDIuOTIxQzExLjUwOTEgMTQyLjg3OSAxMS4zODI4IDE0Mi44NTggMTEuMjQyMiAxNDIuODU4QzExLjA3MDMgMTQyLjg1OCAxMC45MTggMTQyLjg5MSAxMC43ODUyIDE0Mi45NTZDMTAuNjUyMyAxNDMuMDE4IDEwLjU0MDQgMTQzLjExOSAxMC40NDkyIDE0My4yNTdDMTAuMzYwNyAxNDMuMzk1IDEwLjI5MyAxNDMuNTc2IDEwLjI0NjEgMTQzLjhDMTAuMTk5MiAxNDQuMDI0IDEwLjE3NTggMTQ0LjI5NiAxMC4xNzU4IDE0NC42MTZWMTQ1LjcyMUMxMC4xNzU4IDE0NS45NzcgMTAuMTkwMSAxNDYuMjAyIDEwLjIxODggMTQ2LjM5N0MxMC4yNSAxNDYuNTkzIDEwLjI5NTYgMTQ2Ljc2MiAxMC4zNTU1IDE0Ni45MDVDMTAuNDE1NCAxNDcuMDQ2IDEwLjQ4ODMgMTQ3LjE2MiAxMC41NzQyIDE0Ny4yNTNDMTAuNjYwMiAxNDcuMzQ0IDEwLjc1OTEgMTQ3LjQxMiAxMC44NzExIDE0Ny40NTZDMTAuOTg1NyAxNDcuNDk3IDExLjExMiAxNDcuNTE4IDExLjI1IDE0Ny41MThDMTEuNDI3MSAxNDcuNTE4IDExLjU4MiAxNDcuNDg0IDExLjcxNDggMTQ3LjQxN0MxMS44NDc3IDE0Ny4zNDkgMTEuOTU4MyAxNDcuMjQ0IDEyLjA0NjkgMTQ3LjFDMTIuMTM4IDE0Ni45NTUgMTIuMjA1NyAxNDYuNzY4IDEyLjI1IDE0Ni41NDJDMTIuMjk0MyAxNDYuMzEzIDEyLjMxNjQgMTQ2LjAzOSAxMi4zMTY0IDE0NS43MjFaTTE0LjA0MjQgMTQzLjc0MVYxNDMuNDRDMTQuMDQyNCAxNDMuMjI0IDE0LjA4OTIgMTQzLjAyNyAxNC4xODMgMTQyLjg1QzE0LjI3NjcgMTQyLjY3MyAxNC40MTA4IDE0Mi41MzEgMTQuNTg1MyAxNDIuNDI1QzE0Ljc1OTggMTQyLjMxOCAxNC45NjY4IDE0Mi4yNjQgMTUuMjA2NCAxNDIuMjY0QzE1LjQ1MTIgMTQyLjI2NCAxNS42NTk1IDE0Mi4zMTggMTUuODMxNCAxNDIuNDI1QzE2LjAwNTkgMTQyLjUzMSAxNi4xNCAxNDIuNjczIDE2LjIzMzggMTQyLjg1QzE2LjMyNzUgMTQzLjAyNyAxNi4zNzQ0IDE0My4yMjQgMTYuMzc0NCAxNDMuNDRWMTQzLjc0MUMxNi4zNzQ0IDE0My45NTIgMTYuMzI3NSAxNDQuMTQ2IDE2LjIzMzggMTQ0LjMyM0MxNi4xNDI2IDE0NC41IDE2LjAwOTggMTQ0LjY0MiAxNS44MzUzIDE0NC43NDlDMTUuNjYzNSAxNDQuODU2IDE1LjQ1NjQgMTQ0LjkwOSAxNS4yMTQyIDE0NC45MDlDMTQuOTcyIDE0NC45MDkgMTQuNzYyNCAxNDQuODU2IDE0LjU4NTMgMTQ0Ljc0OUMxNC40MTA4IDE0NC42NDIgMTQuMjc2NyAxNDQuNSAxNC4xODMgMTQ0LjMyM0MxNC4wODkyIDE0NC4xNDYgMTQuMDQyNCAxNDMuOTUyIDE0LjA0MjQgMTQzLjc0MVpNMTQuNTg1MyAxNDMuNDRWMTQzLjc0MUMxNC41ODUzIDE0My44NjEgMTQuNjA3NSAxNDMuOTc0IDE0LjY1MTcgMTQ0LjA4MUMxNC42OTg2IDE0NC4xODggMTQuNzY4OSAxNDQuMjc1IDE0Ljg2MjcgMTQ0LjM0M0MxNC45NTY0IDE0NC40MDggMTUuMDczNiAxNDQuNDQgMTUuMjE0MiAxNDQuNDRDMTUuMzU0OSAxNDQuNDQgMTUuNDcwNyAxNDQuNDA4IDE1LjU2MTkgMTQ0LjM0M0MxNS42NTMgMTQ0LjI3NSAxNS43MjA3IDE0NC4xODggMTUuNzY1IDE0NC4wODFDMTUuODA5MyAxNDMuOTc0IDE1LjgzMTQgMTQzLjg2MSAxNS44MzE0IDE0My43NDFWMTQzLjQ0QzE1LjgzMTQgMTQzLjMxOCAxNS44MDggMTQzLjIwMyAxNS43NjExIDE0My4wOTZDMTUuNzE2OCAxNDIuOTg3IDE1LjY0NzggMTQyLjkgMTUuNTU0MSAxNDIuODM1QzE1LjQ2MjkgMTQyLjc2NyAxNS4zNDcgMTQyLjczMyAxNS4yMDY0IDE0Mi43MzNDMTUuMDY4NCAxNDIuNzMzIDE0Ljk1MjUgMTQyLjc2NyAxNC44NTg4IDE0Mi44MzVDMTQuNzY3NiAxNDIuOSAxNC42OTg2IDE0Mi45ODcgMTQuNjUxNyAxNDMuMDk2QzE0LjYwNzUgMTQzLjIwMyAxNC41ODUzIDE0My4zMTggMTQuNTg1MyAxNDMuNDRaTTE2LjgxMTkgMTQ2Ljk0NFYxNDYuNjM5QzE2LjgxMTkgMTQ2LjQyNiAxNi44NTg4IDE0Ni4yMzEgMTYuOTUyNSAxNDYuMDUzQzE3LjA0NjMgMTQ1Ljg3NiAxNy4xODA0IDE0NS43MzQgMTcuMzU0OSAxNDUuNjI4QzE3LjUyOTMgMTQ1LjUyMSAxNy43MzY0IDE0NS40NjggMTcuOTc2IDE0NS40NjhDMTguMjIwNyAxNDUuNDY4IDE4LjQyOTEgMTQ1LjUyMSAxOC42MDEgMTQ1LjYyOEMxOC43NzU0IDE0NS43MzQgMTguOTA5NSAxNDUuODc2IDE5LjAwMzMgMTQ2LjA1M0MxOS4wOTcgMTQ2LjIzMSAxOS4xNDM5IDE0Ni40MjYgMTkuMTQzOSAxNDYuNjM5VjE0Ni45NDRDMTkuMTQzOSAxNDcuMTU4IDE5LjA5NyAxNDcuMzUzIDE5LjAwMzMgMTQ3LjUzQzE4LjkxMjIgMTQ3LjcwNyAxOC43NzkzIDE0Ny44NDkgMTguNjA0OSAxNDcuOTU2QzE4LjQzMyAxNDguMDYzIDE4LjIyNiAxNDguMTE2IDE3Ljk4MzggMTQ4LjExNkMxNy43NDE2IDE0OC4xMTYgMTcuNTMzMiAxNDguMDYzIDE3LjM1ODggMTQ3Ljk1NkMxNy4xODQzIDE0Ny44NDkgMTcuMDQ4OSAxNDcuNzA3IDE2Ljk1MjUgMTQ3LjUzQzE2Ljg1ODggMTQ3LjM1MyAxNi44MTE5IDE0Ny4xNTggMTYuODExOSAxNDYuOTQ0Wk0xNy4zNTQ5IDE0Ni42MzlWMTQ2Ljk0NEMxNy4zNTQ5IDE0Ny4wNjQgMTcuMzc3IDE0Ny4xNzggMTcuNDIxMyAxNDcuMjg4QzE3LjQ2ODEgMTQ3LjM5NSAxNy41Mzg1IDE0Ny40ODIgMTcuNjMyMiAxNDcuNTVDMTcuNzI2IDE0Ny42MTUgMTcuODQzMSAxNDcuNjQ3IDE3Ljk4MzggMTQ3LjY0N0MxOC4xMjQ0IDE0Ny42NDcgMTguMjQwMyAxNDcuNjE1IDE4LjMzMTQgMTQ3LjU1QzE4LjQyNTIgMTQ3LjQ4MiAxOC40OTQyIDE0Ny4zOTUgMTguNTM4NSAxNDcuMjg4QzE4LjU4MjcgMTQ3LjE4MSAxOC42MDQ5IDE0Ny4wNjYgMTguNjA0OSAxNDYuOTQ0VjE0Ni42MzlDMTguNjA0OSAxNDYuNTE3IDE4LjU4MTQgMTQ2LjQwMiAxOC41MzQ1IDE0Ni4yOTZDMTguNDkwMyAxNDYuMTg5IDE4LjQyMTMgMTQ2LjEwMyAxOC4zMjc1IDE0Ni4wMzhDMTguMjM2NCAxNDUuOTcgMTguMTE5MiAxNDUuOTM2IDE3Ljk3NiAxNDUuOTM2QzE3LjgzNzkgMTQ1LjkzNiAxNy43MjIgMTQ1Ljk3IDE3LjYyODMgMTQ2LjAzOEMxNy41MzcyIDE0Ni4xMDMgMTcuNDY4MSAxNDYuMTg5IDE3LjQyMTMgMTQ2LjI5NkMxNy4zNzcgMTQ2LjQwMiAxNy4zNTQ5IDE0Ni41MTcgMTcuMzU0OSAxNDYuNjM5Wk0xOC4xNTU2IDE0My4xNTVMMTUuMzc4MyAxNDcuNkwxNC45NzIgMTQ3LjM0M0wxNy43NDk0IDE0Mi44OTdMMTguMTU1NiAxNDMuMTU1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNMjUgNC4xNjExM0wyMDAgNC4xNjExNiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPHBhdGggZD0iTTI1IDMzLjE2MTFMMjAwIDMzLjE2MTIiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjEyIi8+CjxwYXRoIGQ9Ik0yNSA2MS4xNjExTDIwMCA2MS4xNjEyIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMjUgODkuMTYxMUwyMDAgODkuMTYxMiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPHBhdGggZD0iTTI1IDExOC4xNjFMMjAwIDExOC4xNjEiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjEyIi8+CjxwYXRoIGQ9Ik0zNiA2MkMzNiA2MC44OTU0IDM2Ljg5NTQgNjAgMzggNjBINTJDNTMuMTA0NiA2MCA1NCA2MC44OTU0IDU0IDYyVjE0NkgzNlY2MloiIGZpbGw9IiNGRkMxMDciLz4KPHBhdGggZD0iTTU4IDkyQzU4IDkwLjg5NTQgNTguODk1NCA5MCA2MCA5MEg3NEM3NS4xMDQ2IDkwIDc2IDkwLjg5NTQgNzYgOTJWMTQ2SDU4VjkyWiIgZmlsbD0iIzRDQUY1MCIvPgo8cGF0aCBkPSJNODAgNzlDODAgNzcuODk1NCA4MC44OTU0IDc3IDgyIDc3SDk2Qzk3LjEwNDYgNzcgOTggNzcuODk1NCA5OCA3OVYxNDZIODBWNzlaIiBmaWxsPSIjMjE5NkYzIi8+CjxwYXRoIGQ9Ik0xMjUgNzhDMTI1IDc2Ljg5NTQgMTI1Ljg5NSA3NiAxMjcgNzZIMTQxQzE0Mi4xMDUgNzYgMTQzIDc2Ljg5NTQgMTQzIDc4VjE0NkgxMjVWNzhaIiBmaWxsPSIjRkZDMTA3Ii8+CjxwYXRoIGQ9Ik0xNDcgNTFDMTQ3IDQ5Ljg5NTQgMTQ3Ljg5NSA0OSAxNDkgNDlIMTYzQzE2NC4xMDUgNDkgMTY1IDQ5Ljg5NTQgMTY1IDUxVjE0NkgxNDdWNTFaIiBmaWxsPSIjNENBRjUwIi8+CjxwYXRoIGQ9Ik0xNjkgMzZDMTY5IDM0Ljg5NTQgMTY5Ljg5NSAzNCAxNzEgMzRIMTg1QzE4Ni4xMDUgMzQgMTg3IDM0Ljg5NTQgMTg3IDM2VjE0NkgxNjlWMzZaIiBmaWxsPSIjMjE5NkYzIi8+CjxsaW5lIHgxPSIyMy4yIiB5MT0iMTQ1Ljk2MSIgeDI9IjIwMi44IiB5Mj0iMTQ1Ljk2MSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNyIgc3Ryb2tlLXdpZHRoPSIwLjQiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPGxpbmUgeDE9IjY3IiB5MT0iMTQ4LjA3MiIgeDI9IjY3IiB5Mj0iMTQ3LjI1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNNTkuMDU5MSAxNTIuMDI1VjE1Mi44OTNDNTkuMDU5MSAxNTMuMzU5IDU5LjAxNzQgMTUzLjc1MiA1OC45MzQxIDE1NC4wNzJDNTguODUwNyAxNTQuMzkzIDU4LjczMDkgMTU0LjY1IDU4LjU3NDcgMTU0Ljg0NkM1OC40MTg0IDE1NS4wNDEgNTguMjI5NiAxNTUuMTgzIDU4LjAwODMgMTU1LjI3MUM1Ny43ODk1IDE1NS4zNTcgNTcuNTQyMSAxNTUuNCA1Ny4yNjYxIDE1NS40QzU3LjA0NzMgMTU1LjQgNTYuODQ1NSAxNTUuMzczIDU2LjY2MDYgMTU1LjMxOEM1Ni40NzU3IDE1NS4yNjQgNTYuMzA5MSAxNTUuMTc2IDU2LjE2MDYgMTU1LjA1N0M1Ni4wMTQ4IDE1NC45MzQgNTUuODg5OCAxNTQuNzc1IDU1Ljc4NTYgMTU0LjU4QzU1LjY4MTUgMTU0LjM4NSA1NS42MDIgMTU0LjE0OCA1NS41NDczIDE1My44NjlDNTUuNDkyNyAxNTMuNTkgNTUuNDY1MyAxNTMuMjY1IDU1LjQ2NTMgMTUyLjg5M1YxNTIuMDI1QzU1LjQ2NTMgMTUxLjU1OSA1NS41MDcgMTUxLjE2OSA1NS41OTAzIDE1MC44NTRDNTUuNjc2MiAxNTAuNTM4IDU1Ljc5NzMgMTUwLjI4NiA1NS45NTM2IDE1MC4wOTZDNTYuMTA5OCAxNDkuOTAzIDU2LjI5NzMgMTQ5Ljc2NSA1Ni41MTYxIDE0OS42ODJDNTYuNzM3NCAxNDkuNTk4IDU2Ljk4NDggMTQ5LjU1NyA1Ny4yNTgzIDE0OS41NTdDNTcuNDc5NiAxNDkuNTU3IDU3LjY4MjggMTQ5LjU4NCA1Ny44Njc3IDE0OS42MzlDNTguMDU1MiAxNDkuNjkxIDU4LjIyMTggMTQ5Ljc3NSA1OC4zNjc3IDE0OS44OTNDNTguNTEzNSAxNTAuMDA3IDU4LjYzNzIgMTUwLjE2MSA1OC43Mzg3IDE1MC4zNTRDNTguODQyOSAxNTAuNTQ0IDU4LjkyMjMgMTUwLjc3NyA1OC45NzcgMTUxLjA1M0M1OS4wMzE3IDE1MS4zMjkgNTkuMDU5MSAxNTEuNjUzIDU5LjA1OTEgMTUyLjAyNVpNNTguMzMyNSAxNTMuMDFWMTUxLjkwNEM1OC4zMzI1IDE1MS42NDkgNTguMzE2OSAxNTEuNDI1IDU4LjI4NTYgMTUxLjIzMkM1OC4yNTcgMTUxLjAzNyA1OC4yMTQgMTUwLjg3IDU4LjE1NjcgMTUwLjczMkM1OC4wOTk0IDE1MC41OTQgNTguMDI2NSAxNTAuNDgyIDU3LjkzOCAxNTAuMzk2QzU3Ljg1MiAxNTAuMzExIDU3Ljc1MTggMTUwLjI0OCA1Ny42MzcyIDE1MC4yMDlDNTcuNTI1MiAxNTAuMTY3IDU3LjM5ODkgMTUwLjE0NiA1Ny4yNTgzIDE1MC4xNDZDNTcuMDg2NCAxNTAuMTQ2IDU2LjkzNDEgMTUwLjE3OSA1Ni44MDEyIDE1MC4yNDRDNTYuNjY4NCAxNTAuMzA3IDU2LjU1NjUgMTUwLjQwNyA1Ni40NjUzIDE1MC41NDVDNTYuMzc2OCAxNTAuNjgzIDU2LjMwOTEgMTUwLjg2NCA1Ni4yNjIyIDE1MS4wODhDNTYuMjE1MyAxNTEuMzEyIDU2LjE5MTkgMTUxLjU4NCA1Ni4xOTE5IDE1MS45MDRWMTUzLjAxQzU2LjE5MTkgMTUzLjI2NSA1Ni4yMDYyIDE1My40OSA1Ni4yMzQ4IDE1My42ODZDNTYuMjY2MSAxNTMuODgxIDU2LjMxMTcgMTU0LjA1IDU2LjM3MTYgMTU0LjE5M0M1Ni40MzE1IDE1NC4zMzQgNTYuNTA0NCAxNTQuNDUgNTYuNTkwMyAxNTQuNTQxQzU2LjY3NjIgMTU0LjYzMiA1Ni43NzUyIDE1NC43IDU2Ljg4NzIgMTU0Ljc0NEM1Ny4wMDE4IDE1NC43ODYgNTcuMTI4MSAxNTQuODA3IDU3LjI2NjEgMTU0LjgwN0M1Ny40NDMyIDE1NC44MDcgNTcuNTk4MSAxNTQuNzczIDU3LjczMDkgMTU0LjcwNUM1Ny44NjM3IDE1NC42MzcgNTcuOTc0NCAxNTQuNTMyIDU4LjA2MyAxNTQuMzg5QzU4LjE1NDEgMTU0LjI0MyA1OC4yMjE4IDE1NC4wNTcgNTguMjY2MSAxNTMuODNDNTguMzEwNCAxNTMuNjAxIDU4LjMzMjUgMTUzLjMyNyA1OC4zMzI1IDE1My4wMVpNNjIuNDQ2NCAxNDkuNjA0VjE1NS4zMjJINjEuNzIzN1YxNTAuNTA2TDYwLjI2NjcgMTUxLjAzN1YxNTAuMzg1TDYyLjMzMzEgMTQ5LjYwNEg2Mi40NDY0Wk02Ny42NjI0IDE0OS42MzVWMTU1LjMyMkg2Ni45MDg1VjE0OS42MzVINjcuNjYyNFpNNzAuMDQ1MiAxNTIuMTkzVjE1Mi44MTFINjcuNDk4M1YxNTIuMTkzSDcwLjA0NTJaTTcwLjQzMTkgMTQ5LjYzNVYxNTAuMjUySDY3LjQ5ODNWMTQ5LjYzNUg3MC40MzE5Wk03Mi45NzE2IDE1NS40QzcyLjY3NzMgMTU1LjQgNzIuNDEwNCAxNTUuMzUxIDcyLjE3MDggMTU1LjI1MkM3MS45MzM4IDE1NS4xNSA3MS43Mjk0IDE1NS4wMDggNzEuNTU3NSAxNTQuODI2QzcxLjM4ODMgMTU0LjY0NCA3MS4yNTgxIDE1NC40MjggNzEuMTY2OSAxNTQuMTc4QzcxLjA3NTggMTUzLjkyOCA3MS4wMzAyIDE1My42NTQgNzEuMDMwMiAxNTMuMzU3VjE1My4xOTNDNzEuMDMwMiAxNTIuODUgNzEuMDgxIDE1Mi41NDQgNzEuMTgyNSAxNTIuMjc1QzcxLjI4NDEgMTUyLjAwNSA3MS40MjIxIDE1MS43NzUgNzEuNTk2NiAxNTEuNTg4QzcxLjc3MTEgMTUxLjQgNzEuOTY5IDE1MS4yNTggNzIuMTkwMyAxNTEuMTYyQzcyLjQxMTcgMTUxLjA2NiA3Mi42NDA5IDE1MS4wMTggNzIuODc3OCAxNTEuMDE4QzczLjE3OTkgMTUxLjAxOCA3My40NDAzIDE1MS4wNyA3My42NTkxIDE1MS4xNzRDNzMuODgwNSAxNTEuMjc4IDc0LjA2MTQgMTUxLjQyNCA3NC4yMDIxIDE1MS42MTFDNzQuMzQyNyAxNTEuNzk2IDc0LjQ0NjkgMTUyLjAxNSA3NC41MTQ2IDE1Mi4yNjhDNzQuNTgyMyAxNTIuNTE4IDc0LjYxNjEgMTUyLjc5MSA3NC42MTYxIDE1My4wODhWMTUzLjQxMkg3MS40NTk5VjE1Mi44MjJINzMuODkzNVYxNTIuNzY4QzczLjg4MzEgMTUyLjU4IDczLjg0NCAxNTIuMzk4IDczLjc3NjMgMTUyLjIyMUM3My43MTEyIDE1Mi4wNDQgNzMuNjA3IDE1MS44OTggNzMuNDYzOCAxNTEuNzgzQzczLjMyMDYgMTUxLjY2OSA3My4xMjUyIDE1MS42MTEgNzIuODc3OCAxNTEuNjExQzcyLjcxMzggMTUxLjYxMSA3Mi41NjI3IDE1MS42NDYgNzIuNDI0NyAxNTEuNzE3QzcyLjI4NjcgMTUxLjc4NSA3Mi4xNjgyIDE1MS44ODYgNzIuMDY5MyAxNTIuMDIxQzcxLjk3MDMgMTUyLjE1NyA3MS44OTM1IDE1Mi4zMjIgNzEuODM4OCAxNTIuNTE4QzcxLjc4NDEgMTUyLjcxMyA3MS43NTY4IDE1Mi45MzggNzEuNzU2OCAxNTMuMTkzVjE1My4zNTdDNzEuNzU2OCAxNTMuNTU4IDcxLjc4NDEgMTUzLjc0NyA3MS44Mzg4IDE1My45MjRDNzEuODk2MSAxNTQuMDk4IDcxLjk3ODEgMTU0LjI1MiA3Mi4wODQ5IDE1NC4zODVDNzIuMTk0MyAxNTQuNTE4IDcyLjMyNTggMTU0LjYyMiA3Mi40Nzk0IDE1NC42OTdDNzIuNjM1NyAxNTQuNzczIDcyLjgxMjcgMTU0LjgxMSA3My4wMTA3IDE1NC44MTFDNzMuMjY1OSAxNTQuODExIDczLjQ4MiAxNTQuNzU4IDczLjY1OTEgMTU0LjY1NEM3My44MzYyIDE1NC41NSA3My45OTExIDE1NC40MTEgNzQuMTIzOSAxNTQuMjM2TDc0LjU2MTQgMTU0LjU4NEM3NC40NzAzIDE1NC43MjIgNzQuMzU0NCAxNTQuODU0IDc0LjIxMzggMTU0Ljk3OUM3NC4wNzMyIDE1NS4xMDQgNzMuOSAxNTUuMjA1IDczLjY5NDMgMTU1LjI4M0M3My40OTExIDE1NS4zNjEgNzMuMjUwMiAxNTUuNCA3Mi45NzE2IDE1NS40Wk03NS41Mzg2IDE0OS4zMjJINzYuMjY1MlYxNTQuNTAyTDc2LjIwMjcgMTU1LjMyMkg3NS41Mzg2VjE0OS4zMjJaTTc5LjEyMDYgMTUzLjE3NFYxNTMuMjU2Qzc5LjEyMDYgMTUzLjU2MyA3OS4wODQyIDE1My44NDggNzkuMDExMyAxNTQuMTExQzc4LjkzODMgMTU0LjM3MiA3OC44MzE2IDE1NC41OTggNzguNjkwOSAxNTQuNzkxQzc4LjU1MDMgMTU0Ljk4NCA3OC4zNzg0IDE1NS4xMzMgNzguMTc1MyAxNTUuMjRDNzcuOTcyMiAxNTUuMzQ3IDc3LjczOTEgMTU1LjQgNzcuNDc2MSAxNTUuNEM3Ny4yMDc5IDE1NS40IDc2Ljk3MjIgMTU1LjM1NSA3Ni43NjkxIDE1NS4yNjRDNzYuNTY4NSAxNTUuMTcgNzYuMzk5MyAxNTUuMDM2IDc2LjI2MTMgMTU0Ljg2MUM3Ni4xMjMyIDE1NC42ODcgNzYuMDEyNiAxNTQuNDc2IDc1LjkyOTIgMTU0LjIyOUM3NS44NDg1IDE1My45ODEgNzUuNzkyNSAxNTMuNzAyIDc1Ljc2MTMgMTUzLjM5M1YxNTMuMDMzQzc1Ljc5MjUgMTUyLjcyMSA3NS44NDg1IDE1Mi40NDEgNzUuOTI5MiAxNTIuMTkzQzc2LjAxMjYgMTUxLjk0NiA3Ni4xMjMyIDE1MS43MzUgNzYuMjYxMyAxNTEuNTYxQzc2LjM5OTMgMTUxLjM4MyA3Ni41Njg1IDE1MS4yNDkgNzYuNzY5MSAxNTEuMTU4Qzc2Ljk2OTYgMTUxLjA2NCA3Ny4yMDI3IDE1MS4wMTggNzcuNDY4MyAxNTEuMDE4Qzc3LjczMzkgMTUxLjAxOCA3Ny45Njk2IDE1MS4wNyA3OC4xNzUzIDE1MS4xNzRDNzguMzgxIDE1MS4yNzUgNzguNTUyOSAxNTEuNDIxIDc4LjY5MDkgMTUxLjYxMUM3OC44MzE2IDE1MS44MDEgNzguOTM4MyAxNTIuMDI5IDc5LjAxMTMgMTUyLjI5NUM3OS4wODQyIDE1Mi41NTggNzkuMTIwNiAxNTIuODUxIDc5LjEyMDYgMTUzLjE3NFpNNzguMzk0MSAxNTMuMjU2VjE1My4xNzRDNzguMzk0MSAxNTIuOTYzIDc4LjM3NDUgMTUyLjc2NSA3OC4zMzU1IDE1Mi41OEM3OC4yOTY0IDE1Mi4zOTMgNzguMjMzOSAxNTIuMjI5IDc4LjE0OCAxNTIuMDg4Qzc4LjA2MiAxNTEuOTQ1IDc3Ljk0ODggMTUxLjgzMyA3Ny44MDgxIDE1MS43NTJDNzcuNjY3NSAxNTEuNjY5IDc3LjQ5NDMgMTUxLjYyNyA3Ny4yODg2IDE1MS42MjdDNzcuMTA2MyAxNTEuNjI3IDc2Ljk0NzUgMTUxLjY1OCA3Ni44MTIgMTUxLjcyMUM3Ni42NzkyIDE1MS43ODMgNzYuNTY1OSAxNTEuODY4IDc2LjQ3MjIgMTUxLjk3NUM3Ni4zNzg0IDE1Mi4wNzkgNzYuMzAxNiAxNTIuMTk5IDc2LjI0MTcgMTUyLjMzNEM3Ni4xODQ0IDE1Mi40NjcgNzYuMTQxNSAxNTIuNjA1IDc2LjExMjggMTUyLjc0OFYxNTMuNjg5Qzc2LjE1NDUgMTUzLjg3MiA3Ni4yMjIyIDE1NC4wNDggNzYuMzE1OSAxNTQuMjE3Qzc2LjQxMjMgMTU0LjM4MyA3Ni41Mzk5IDE1NC41MiA3Ni42OTg4IDE1NC42MjdDNzYuODYwMiAxNTQuNzM0IDc3LjA1OTQgMTU0Ljc4NyA3Ny4yOTY0IDE1NC43ODdDNzcuNDkxNyAxNTQuNzg3IDc3LjY1ODQgMTU0Ljc0OCA3Ny43OTY0IDE1NC42N0M3Ny45MzcgMTU0LjU4OSA3OC4wNTAzIDE1NC40NzkgNzguMTM2MyAxNTQuMzM4Qzc4LjIyNDggMTU0LjE5NyA3OC4yODk5IDE1NC4wMzUgNzguMzMxNiAxNTMuODVDNzguMzczMiAxNTMuNjY1IDc4LjM5NDEgMTUzLjQ2NyA3OC4zOTQxIDE1My4yNTZaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMV80MTgzXzkwODc3KSI+CjxsaW5lIHgxPSIxNTUuNSIgeTE9IjE0Ny4wNzIiIHgyPSIxNTUuNSIgeTI9IjE0Ni4yNSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNSIgc3Ryb2tlLXdpZHRoPSIwLjUiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPHBhdGggZD0iTTE0Ny41NTkgMTUyLjAyNVYxNTIuODkyQzE0Ny41NTkgMTUzLjM1OCAxNDcuNTE3IDE1My43NTIgMTQ3LjQzNCAxNTQuMDcyQzE0Ny4zNTEgMTU0LjM5MiAxNDcuMjMxIDE1NC42NSAxNDcuMDc1IDE1NC44NDVDMTQ2LjkxOCAxNTUuMDQxIDE0Ni43MyAxNTUuMTgzIDE0Ni41MDggMTU1LjI3MUMxNDYuMjkgMTU1LjM1NyAxNDYuMDQyIDE1NS40IDE0NS43NjYgMTU1LjRDMTQ1LjU0NyAxNTUuNCAxNDUuMzQ2IDE1NS4zNzMgMTQ1LjE2MSAxNTUuMzE4QzE0NC45NzYgMTU1LjI2MyAxNDQuODA5IDE1NS4xNzYgMTQ0LjY2MSAxNTUuMDU2QzE0NC41MTUgMTU0LjkzNCAxNDQuMzkgMTU0Ljc3NSAxNDQuMjg2IDE1NC41OEMxNDQuMTgxIDE1NC4zODUgMTQ0LjEwMiAxNTQuMTQ4IDE0NC4wNDcgMTUzLjg2OUMxNDMuOTkzIDE1My41OSAxNDMuOTY1IDE1My4yNjUgMTQzLjk2NSAxNTIuODkyVjE1Mi4wMjVDMTQzLjk2NSAxNTEuNTU5IDE0NC4wMDcgMTUxLjE2OCAxNDQuMDkgMTUwLjg1M0MxNDQuMTc2IDE1MC41MzggMTQ0LjI5NyAxNTAuMjg2IDE0NC40NTQgMTUwLjA5NUMxNDQuNjEgMTQ5LjkwMyAxNDQuNzk3IDE0OS43NjUgMTQ1LjAxNiAxNDkuNjgxQzE0NS4yMzcgMTQ5LjU5OCAxNDUuNDg1IDE0OS41NTYgMTQ1Ljc1OCAxNDkuNTU2QzE0NS45OCAxNDkuNTU2IDE0Ni4xODMgMTQ5LjU4NCAxNDYuMzY4IDE0OS42MzhDMTQ2LjU1NSAxNDkuNjkxIDE0Ni43MjIgMTQ5Ljc3NSAxNDYuODY4IDE0OS44OTJDMTQ3LjAxMyAxNTAuMDA3IDE0Ny4xMzcgMTUwLjE2MSAxNDcuMjM5IDE1MC4zNTNDMTQ3LjM0MyAxNTAuNTQzIDE0Ny40MjIgMTUwLjc3NiAxNDcuNDc3IDE1MS4wNTJDMTQ3LjUzMiAxNTEuMzI5IDE0Ny41NTkgMTUxLjY1MyAxNDcuNTU5IDE1Mi4wMjVaTTE0Ni44MzIgMTUzLjAxVjE1MS45MDRDMTQ2LjgzMiAxNTEuNjQ5IDE0Ni44MTcgMTUxLjQyNSAxNDYuNzg2IDE1MS4yMzJDMTQ2Ljc1NyAxNTEuMDM3IDE0Ni43MTQgMTUwLjg3IDE0Ni42NTcgMTUwLjczMkMxNDYuNTk5IDE1MC41OTQgMTQ2LjUyNyAxNTAuNDgyIDE0Ni40MzggMTUwLjM5NkMxNDYuMzUyIDE1MC4zMSAxNDYuMjUyIDE1MC4yNDggMTQ2LjEzNyAxNTAuMjA5QzE0Ni4wMjUgMTUwLjE2NyAxNDUuODk5IDE1MC4xNDYgMTQ1Ljc1OCAxNTAuMTQ2QzE0NS41ODYgMTUwLjE0NiAxNDUuNDM0IDE1MC4xNzkgMTQ1LjMwMSAxNTAuMjQ0QzE0NS4xNjggMTUwLjMwNiAxNDUuMDU2IDE1MC40MDcgMTQ0Ljk2NSAxNTAuNTQ1QzE0NC44NzcgMTUwLjY4MyAxNDQuODA5IDE1MC44NjQgMTQ0Ljc2MiAxNTEuMDg4QzE0NC43MTUgMTUxLjMxMiAxNDQuNjkyIDE1MS41ODQgMTQ0LjY5MiAxNTEuOTA0VjE1My4wMUMxNDQuNjkyIDE1My4yNjUgMTQ0LjcwNiAxNTMuNDkgMTQ0LjczNSAxNTMuNjg1QzE0NC43NjYgMTUzLjg4MSAxNDQuODEyIDE1NC4wNSAxNDQuODcyIDE1NC4xOTNDMTQ0LjkzMSAxNTQuMzM0IDE0NS4wMDQgMTU0LjQ1IDE0NS4wOSAxNTQuNTQxQzE0NS4xNzYgMTU0LjYzMiAxNDUuMjc1IDE1NC43IDE0NS4zODcgMTU0Ljc0NEMxNDUuNTAyIDE1NC43ODYgMTQ1LjYyOCAxNTQuODA2IDE0NS43NjYgMTU0LjgwNkMxNDUuOTQzIDE1NC44MDYgMTQ2LjA5OCAxNTQuNzczIDE0Ni4yMzEgMTU0LjcwNUMxNDYuMzY0IDE1NC42MzcgMTQ2LjQ3NCAxNTQuNTMyIDE0Ni41NjMgMTU0LjM4OEMxNDYuNjU0IDE1NC4yNDMgMTQ2LjcyMiAxNTQuMDU2IDE0Ni43NjYgMTUzLjgzQzE0Ni44MSAxNTMuNjAxIDE0Ni44MzIgMTUzLjMyNyAxNDYuODMyIDE1My4wMVpNMTUyLjI5OCAxNTQuNzI4VjE1NS4zMjJIMTQ4LjU3NVYxNTQuODAyTDE1MC40MzkgMTUyLjcyOEMxNTAuNjY4IDE1Mi40NzMgMTUwLjg0NSAxNTIuMjU3IDE1MC45NyAxNTIuMDhDMTUxLjA5NyAxNTEuOSAxNTEuMTg2IDE1MS43NCAxNTEuMjM1IDE1MS41OTlDMTUxLjI4OCAxNTEuNDU2IDE1MS4zMTQgMTUxLjMxIDE1MS4zMTQgMTUxLjE2MkMxNTEuMzE0IDE1MC45NzQgMTUxLjI3NCAxNTAuODA1IDE1MS4xOTYgMTUwLjY1NEMxNTEuMTIxIDE1MC41IDE1MS4wMDkgMTUwLjM3OCAxNTAuODYgMTUwLjI4N0MxNTAuNzEyIDE1MC4xOTYgMTUwLjUzMiAxNTAuMTUgMTUwLjMyMSAxNTAuMTVDMTUwLjA2OSAxNTAuMTUgMTQ5Ljg1OCAxNTAuMiAxNDkuNjg5IDE1MC4yOTlDMTQ5LjUyMiAxNTAuMzk1IDE0OS4zOTcgMTUwLjUzIDE0OS4zMTQgMTUwLjcwNUMxNDkuMjMgMTUwLjg3OSAxNDkuMTg5IDE1MS4wOCAxNDkuMTg5IDE1MS4zMDZIMTQ4LjQ2NkMxNDguNDY2IDE1MC45ODYgMTQ4LjUzNiAxNTAuNjkzIDE0OC42NzcgMTUwLjQyN0MxNDguODE3IDE1MC4xNjIgMTQ5LjAyNiAxNDkuOTUxIDE0OS4zMDIgMTQ5Ljc5NUMxNDkuNTc4IDE0OS42MzYgMTQ5LjkxOCAxNDkuNTU2IDE1MC4zMjEgMTQ5LjU1NkMxNTAuNjgxIDE0OS41NTYgMTUwLjk4OCAxNDkuNjIgMTUxLjI0MyAxNDkuNzQ4QzE1MS40OTggMTQ5Ljg3MyAxNTEuNjk0IDE1MC4wNSAxNTEuODI5IDE1MC4yNzlDMTUxLjk2NyAxNTAuNTA2IDE1Mi4wMzYgMTUwLjc3MSAxNTIuMDM2IDE1MS4wNzZDMTUyLjAzNiAxNTEuMjQzIDE1Mi4wMDggMTUxLjQxMiAxNTEuOTUgMTUxLjU4NEMxNTEuODk2IDE1MS43NTMgMTUxLjgxOSAxNTEuOTIyIDE1MS43MiAxNTIuMDkyQzE1MS42MjMgMTUyLjI2MSAxNTEuNTEgMTUyLjQyNyAxNTEuMzggMTUyLjU5MkMxNTEuMjUyIDE1Mi43NTYgMTUxLjExNiAxNTIuOTE3IDE1MC45NyAxNTMuMDc2TDE0OS40NDYgMTU0LjcyOEgxNTIuMjk4Wk0xNTYuMTYyIDE0OS42MzVWMTU1LjMyMkgxNTUuNDA5VjE0OS42MzVIMTU2LjE2MlpNMTU4LjU0NSAxNTIuMTkzVjE1Mi44MUgxNTUuOTk4VjE1Mi4xOTNIMTU4LjU0NVpNMTU4LjkzMiAxNDkuNjM1VjE1MC4yNTJIMTU1Ljk5OFYxNDkuNjM1SDE1OC45MzJaTTE2MS40NzIgMTU1LjRDMTYxLjE3NyAxNTUuNCAxNjAuOTEgMTU1LjM1MSAxNjAuNjcxIDE1NS4yNTJDMTYwLjQzNCAxNTUuMTUgMTYwLjIyOSAxNTUuMDA4IDE2MC4wNTggMTU0LjgyNkMxNTkuODg4IDE1NC42NDQgMTU5Ljc1OCAxNTQuNDI3IDE1OS42NjcgMTU0LjE3N0MxNTkuNTc2IDE1My45MjcgMTU5LjUzIDE1My42NTQgMTU5LjUzIDE1My4zNTdWMTUzLjE5M0MxNTkuNTMgMTUyLjg0OSAxNTkuNTgxIDE1Mi41NDMgMTU5LjY4MyAxNTIuMjc1QzE1OS43ODQgMTUyLjAwNCAxNTkuOTIyIDE1MS43NzUgMTYwLjA5NyAxNTEuNTg4QzE2MC4yNzEgMTUxLjQgMTYwLjQ2OSAxNTEuMjU4IDE2MC42OSAxNTEuMTYyQzE2MC45MTIgMTUxLjA2NiAxNjEuMTQxIDE1MS4wMTcgMTYxLjM3OCAxNTEuMDE3QzE2MS42OCAxNTEuMDE3IDE2MS45NCAxNTEuMDY5IDE2Mi4xNTkgMTUxLjE3NEMxNjIuMzggMTUxLjI3OCAxNjIuNTYxIDE1MS40MjQgMTYyLjcwMiAxNTEuNjExQzE2Mi44NDMgMTUxLjc5NiAxNjIuOTQ3IDE1Mi4wMTUgMTYzLjAxNSAxNTIuMjY3QzE2My4wODIgMTUyLjUxNyAxNjMuMTE2IDE1Mi43OTEgMTYzLjExNiAxNTMuMDg4VjE1My40MTJIMTU5Ljk2VjE1Mi44MjJIMTYyLjM5M1YxNTIuNzY3QzE2Mi4zODMgMTUyLjU4IDE2Mi4zNDQgMTUyLjM5OCAxNjIuMjc2IDE1Mi4yMkMxNjIuMjExIDE1Mi4wNDMgMTYyLjEwNyAxNTEuODk4IDE2MS45NjQgMTUxLjc4M0MxNjEuODIxIDE1MS42NjggMTYxLjYyNSAxNTEuNjExIDE2MS4zNzggMTUxLjYxMUMxNjEuMjE0IDE1MS42MTEgMTYxLjA2MyAxNTEuNjQ2IDE2MC45MjUgMTUxLjcxN0MxNjAuNzg3IDE1MS43ODQgMTYwLjY2OCAxNTEuODg2IDE2MC41NjkgMTUyLjAyMUMxNjAuNDcgMTUyLjE1NyAxNjAuMzkzIDE1Mi4zMjIgMTYwLjMzOSAxNTIuNTE3QzE2MC4yODQgMTUyLjcxMyAxNjAuMjU3IDE1Mi45MzggMTYwLjI1NyAxNTMuMTkzVjE1My4zNTdDMTYwLjI1NyAxNTMuNTU4IDE2MC4yODQgMTUzLjc0NyAxNjAuMzM5IDE1My45MjRDMTYwLjM5NiAxNTQuMDk4IDE2MC40NzggMTU0LjI1MiAxNjAuNTg1IDE1NC4zODVDMTYwLjY5NCAxNTQuNTE3IDE2MC44MjYgMTU0LjYyMiAxNjAuOTc5IDE1NC42OTdDMTYxLjEzNiAxNTQuNzczIDE2MS4zMTMgMTU0LjgxIDE2MS41MTEgMTU0LjgxQzE2MS43NjYgMTU0LjgxIDE2MS45ODIgMTU0Ljc1OCAxNjIuMTU5IDE1NC42NTRDMTYyLjMzNiAxNTQuNTUgMTYyLjQ5MSAxNTQuNDExIDE2Mi42MjQgMTU0LjIzNkwxNjMuMDYxIDE1NC41ODRDMTYyLjk3IDE1NC43MjIgMTYyLjg1NCAxNTQuODUzIDE2Mi43MTQgMTU0Ljk3OEMxNjIuNTczIDE1NS4xMDMgMTYyLjQgMTU1LjIwNSAxNjIuMTk0IDE1NS4yODNDMTYxLjk5MSAxNTUuMzYxIDE2MS43NSAxNTUuNCAxNjEuNDcyIDE1NS40Wk0xNjQuMDM5IDE0OS4zMjJIMTY0Ljc2NVYxNTQuNTAyTDE2NC43MDMgMTU1LjMyMkgxNjQuMDM5VjE0OS4zMjJaTTE2Ny42MjEgMTUzLjE3NFYxNTMuMjU2QzE2Ny42MjEgMTUzLjU2MyAxNjcuNTg0IDE1My44NDggMTY3LjUxMSAxNTQuMTExQzE2Ny40MzggMTU0LjM3MiAxNjcuMzMyIDE1NC41OTggMTY3LjE5MSAxNTQuNzkxQzE2Ny4wNSAxNTQuOTgzIDE2Ni44NzggMTU1LjEzMyAxNjYuNjc1IDE1NS4yNEMxNjYuNDcyIDE1NS4zNDcgMTY2LjIzOSAxNTUuNCAxNjUuOTc2IDE1NS40QzE2NS43MDggMTU1LjQgMTY1LjQ3MiAxNTUuMzU1IDE2NS4yNjkgMTU1LjI2M0MxNjUuMDY5IDE1NS4xNyAxNjQuODk5IDE1NS4wMzYgMTY0Ljc2MSAxNTQuODYxQzE2NC42MjMgMTU0LjY4NyAxNjQuNTEzIDE1NC40NzYgMTY0LjQyOSAxNTQuMjI4QzE2NC4zNDggMTUzLjk4MSAxNjQuMjkzIDE1My43MDIgMTY0LjI2MSAxNTMuMzkyVjE1My4wMzNDMTY0LjI5MyAxNTIuNzIgMTY0LjM0OCAxNTIuNDQxIDE2NC40MjkgMTUyLjE5M0MxNjQuNTEzIDE1MS45NDYgMTY0LjYyMyAxNTEuNzM1IDE2NC43NjEgMTUxLjU2QzE2NC44OTkgMTUxLjM4MyAxNjUuMDY5IDE1MS4yNDkgMTY1LjI2OSAxNTEuMTU4QzE2NS40NyAxNTEuMDY0IDE2NS43MDMgMTUxLjAxNyAxNjUuOTY4IDE1MS4wMTdDMTY2LjIzNCAxNTEuMDE3IDE2Ni40NyAxNTEuMDY5IDE2Ni42NzUgMTUxLjE3NEMxNjYuODgxIDE1MS4yNzUgMTY3LjA1MyAxNTEuNDIxIDE2Ny4xOTEgMTUxLjYxMUMxNjcuMzMyIDE1MS44MDEgMTY3LjQzOCAxNTIuMDI5IDE2Ny41MTEgMTUyLjI5NUMxNjcuNTg0IDE1Mi41NTggMTY3LjYyMSAxNTIuODUxIDE2Ny42MjEgMTUzLjE3NFpNMTY2Ljg5NCAxNTMuMjU2VjE1My4xNzRDMTY2Ljg5NCAxNTIuOTYzIDE2Ni44NzUgMTUyLjc2NSAxNjYuODM1IDE1Mi41OEMxNjYuNzk2IDE1Mi4zOTIgMTY2LjczNCAxNTIuMjI4IDE2Ni42NDggMTUyLjA4OEMxNjYuNTYyIDE1MS45NDQgMTY2LjQ0OSAxNTEuODMyIDE2Ni4zMDggMTUxLjc1MkMxNjYuMTY4IDE1MS42NjggMTY1Ljk5NCAxNTEuNjI3IDE2NS43ODkgMTUxLjYyN0MxNjUuNjA2IDE1MS42MjcgMTY1LjQ0NyAxNTEuNjU4IDE2NS4zMTIgMTUxLjcyQzE2NS4xNzkgMTUxLjc4MyAxNjUuMDY2IDE1MS44NjggMTY0Ljk3MiAxNTEuOTc0QzE2NC44NzggMTUyLjA3OSAxNjQuODAyIDE1Mi4xOTggMTY0Ljc0MiAxNTIuMzM0QzE2NC42ODQgMTUyLjQ2NyAxNjQuNjQxIDE1Mi42MDUgMTY0LjYxMyAxNTIuNzQ4VjE1My42ODlDMTY0LjY1NCAxNTMuODcyIDE2NC43MjIgMTU0LjA0NyAxNjQuODE2IDE1NC4yMTdDMTY0LjkxMiAxNTQuMzgzIDE2NS4wNCAxNTQuNTIgMTY1LjE5OSAxNTQuNjI3QzE2NS4zNiAxNTQuNzMzIDE2NS41NTkgMTU0Ljc4NyAxNjUuNzk2IDE1NC43ODdDMTY1Ljk5MiAxNTQuNzg3IDE2Ni4xNTggMTU0Ljc0OCAxNjYuMjk2IDE1NC42N0MxNjYuNDM3IDE1NC41ODkgMTY2LjU1IDE1NC40NzggMTY2LjYzNiAxNTQuMzM4QzE2Ni43MjUgMTU0LjE5NyAxNjYuNzkgMTU0LjAzNCAxNjYuODMyIDE1My44NDlDMTY2Ljg3MyAxNTMuNjY0IDE2Ni44OTQgMTUzLjQ2NyAxNjYuODk0IDE1My4yNTZaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjwvZz4KPC9nPgo8ZGVmcz4KPGNsaXBQYXRoIGlkPSJjbGlwMF80MTgzXzkwODc3Ij4KPHJlY3Qgd2lkdGg9IjIwMCIgaGVpZ2h0PSIxNjAiIGZpbGw9IndoaXRlIi8+CjwvY2xpcFBhdGg+CjxjbGlwUGF0aCBpZD0iY2xpcDFfNDE4M185MDg3NyI+CjxyZWN0IHdpZHRoPSI4OC41IiBoZWlnaHQ9IjEwLjMyMiIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDExMS41IDE0NikiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4K", + "description": "Displays changes to time-series data over time—for example, temperature or humidity readings.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "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 chartType: 'bar',\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n dataKeySettingsFunction: TbTimeSeriesChart.dataKeySettings('bar'),\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries', units: '°C', decimals: 0 }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-time-series-chart-widget-settings", + "dataKeySettingsDirective": "tb-time-series-chart-key-settings", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-time-series-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"type\":\"bar\"},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#FFC107\",\"settings\":{\"type\":\"bar\"},\"_hash\":0.5534217244004682,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":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\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"top\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"yAxis\":{\"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)\",\"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)\"},\"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)\",\"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)\"},\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm:ss\",\"lastUpdateAgo\":false,\"custom\":false},\"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,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Bar 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\"}" + }, + "tags": [ + "chart", + "time series", + "time-series", + "bar", + "bar chart" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/line_chart.json b/application/src/main/data/json/system/widget_types/line_chart.json new file mode 100644 index 0000000000..5fff201c28 --- /dev/null +++ b/application/src/main/data/json/system/widget_types/line_chart.json @@ -0,0 +1,32 @@ +{ + "fqn": "line_chart", + "name": "Line chart", + "deprecated": false, + "image": "tb-image:Y2hhcnRfKDEpLnN2Zw==:IkxpbmUgY2hhcnQiIHN5c3RlbSB3aWRnZXQgaW1hZ2U=;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF80MTg0Xzk0NTI3KSI+CjxwYXRoIGQ9Ik0yLjg0NzY2IDEuMjgxMjVWN0gyLjEyNVYyLjE4MzU5TDAuNjY3OTY5IDIuNzE0ODRWMi4wNjI1TDIuNzM0MzggMS4yODEyNUgyLjg0NzY2Wk04LjY3NTE3IDMuNzAzMTJWNC41NzAzMUM4LjY3NTE3IDUuMDM2NDYgOC42MzM1MSA1LjQyOTY5IDguNTUwMTcgNS43NUM4LjQ2Njg0IDYuMDcwMzEgOC4zNDcwNSA2LjMyODEyIDguMTkwOCA2LjUyMzQ0QzguMDM0NTUgNi43MTg3NSA3Ljg0NTc1IDYuODYwNjggNy42MjQzOSA2Ljk0OTIyQzcuNDA1NjQgNy4wMzUxNiA3LjE1ODI1IDcuMDc4MTIgNi44ODIyIDcuMDc4MTJDNi42NjM0NSA3LjA3ODEyIDYuNDYxNjMgNy4wNTA3OCA2LjI3NjczIDYuOTk2MDlDNi4wOTE4NCA2Ljk0MTQxIDUuOTI1MTcgNi44NTQxNyA1Ljc3NjczIDYuNzM0MzhDNS42MzA5IDYuNjExOTggNS41MDU5IDYuNDUzMTIgNS40MDE3MyA2LjI1NzgxQzUuMjk3NTcgNi4wNjI1IDUuMjE4MTQgNS44MjU1MiA1LjE2MzQ1IDUuNTQ2ODhDNS4xMDg3NyA1LjI2ODIzIDUuMDgxNDIgNC45NDI3MSA1LjA4MTQyIDQuNTcwMzFWMy43MDMxMkM1LjA4MTQyIDMuMjM2OTggNS4xMjMwOSAyLjg0NjM1IDUuMjA2NDIgMi41MzEyNUM1LjI5MjM2IDIuMjE2MTUgNS40MTM0NSAxLjk2MzU0IDUuNTY5NyAxLjc3MzQ0QzUuNzI1OTUgMS41ODA3MyA1LjkxMzQ1IDEuNDQyNzEgNi4xMzIyIDEuMzU5MzhDNi4zNTM1NiAxLjI3NjA0IDYuNjAwOTUgMS4yMzQzOCA2Ljg3NDM5IDEuMjM0MzhDNy4wOTU3NSAxLjIzNDM4IDcuMjk4ODcgMS4yNjE3MiA3LjQ4Mzc3IDEuMzE2NDFDNy42NzEyNyAxLjM2ODQ5IDcuODM3OTMgMS40NTMxMiA3Ljk4Mzc3IDEuNTcwMzFDOC4xMjk2IDEuNjg0OSA4LjI1MzMgMS44Mzg1NCA4LjM1NDg2IDIuMDMxMjVDOC40NTkwMyAyLjIyMTM1IDguNTM4NDUgMi40NTQ0MyA4LjU5MzE0IDIuNzMwNDdDOC42NDc4MyAzLjAwNjUxIDguNjc1MTcgMy4zMzA3MyA4LjY3NTE3IDMuNzAzMTJaTTcuOTQ4NjEgNC42ODc1VjMuNTgyMDNDNy45NDg2MSAzLjMyNjgyIDcuOTMyOTggMy4xMDI4NiA3LjkwMTczIDIuOTEwMTZDNy44NzMwOSAyLjcxNDg0IDcuODMwMTIgMi41NDgxOCA3Ljc3MjgzIDIuNDEwMTZDNy43MTU1NCAyLjI3MjE0IDcuNjQyNjIgMi4xNjAxNiA3LjU1NDA4IDIuMDc0MjJDNy40NjgxNCAxLjk4ODI4IDcuMzY3ODggMS45MjU3OCA3LjI1MzMgMS44ODY3MkM3LjE0MTMyIDEuODQ1MDUgNy4wMTUwMiAxLjgyNDIyIDYuODc0MzkgMS44MjQyMkM2LjcwMjUyIDEuODI0MjIgNi41NTAxNyAxLjg1Njc3IDYuNDE3MzYgMS45MjE4OEM2LjI4NDU1IDEuOTg0MzggNi4xNzI1NyAyLjA4NDY0IDYuMDgxNDIgMi4yMjI2NkM1Ljk5Mjg4IDIuMzYwNjggNS45MjUxNyAyLjU0MTY3IDUuODc4MyAyLjc2NTYyQzUuODMxNDIgMi45ODk1OCA1LjgwNzk4IDMuMjYxNzIgNS44MDc5OCAzLjU4MjAzVjQuNjg3NUM1LjgwNzk4IDQuOTQyNzEgNS44MjIzMSA1LjE2Nzk3IDUuODUwOTUgNS4zNjMyOEM1Ljg4MjIgNS41NTg1OSA1LjkyNzc4IDUuNzI3ODYgNS45ODc2NyA1Ljg3MTA5QzYuMDQ3NTcgNi4wMTE3MiA2LjEyMDQ4IDYuMTI3NiA2LjIwNjQyIDYuMjE4NzVDNi4yOTIzNiA2LjMwOTkgNi4zOTEzMiA2LjM3NzYgNi41MDMzIDYuNDIxODhDNi42MTc4OCA2LjQ2MzU0IDYuNzQ0MTggNi40ODQzOCA2Ljg4MjIgNi40ODQzOEM3LjA1OTI5IDYuNDg0MzggNy4yMTQyMyA2LjQ1MDUyIDcuMzQ3MDUgNi4zODI4MUM3LjQ3OTg2IDYuMzE1MSA3LjU5MDU0IDYuMjA5NjQgNy42NzkwOCA2LjA2NjQxQzcuNzcwMjIgNS45MjA1NyA3LjgzNzkzIDUuNzM0MzggNy44ODIyIDUuNTA3ODFDNy45MjY0NyA1LjI3ODY1IDcuOTQ4NjEgNS4wMDUyMSA3Ljk0ODYxIDQuNjg3NVpNMTMuMzA3NCAzLjcwMzEyVjQuNTcwMzFDMTMuMzA3NCA1LjAzNjQ2IDEzLjI2NTcgNS40Mjk2OSAxMy4xODI0IDUuNzVDMTMuMDk5IDYuMDcwMzEgMTIuOTc5MyA2LjMyODEyIDEyLjgyMyA2LjUyMzQ0QzEyLjY2NjggNi43MTg3NSAxMi40Nzc5IDYuODYwNjggMTIuMjU2NiA2Ljk0OTIyQzEyLjAzNzggNy4wMzUxNiAxMS43OTA0IDcuMDc4MTIgMTEuNTE0NCA3LjA3ODEyQzExLjI5NTcgNy4wNzgxMiAxMS4wOTM4IDcuMDUwNzggMTAuOTA4OSA2Ljk5NjA5QzEwLjcyNCA2Ljk0MTQxIDEwLjU1NzQgNi44NTQxNyAxMC40MDg5IDYuNzM0MzhDMTAuMjYzMSA2LjYxMTk4IDEwLjEzODEgNi40NTMxMiAxMC4wMzM5IDYuMjU3ODFDOS45Mjk3NyA2LjA2MjUgOS44NTAzNCA1LjgyNTUyIDkuNzk1NjYgNS41NDY4OEM5Ljc0MDk3IDUuMjY4MjMgOS43MTM2MyA0Ljk0MjcxIDkuNzEzNjMgNC41NzAzMVYzLjcwMzEyQzkuNzEzNjMgMy4yMzY5OCA5Ljc1NTI5IDIuODQ2MzUgOS44Mzg2MyAyLjUzMTI1QzkuOTI0NTYgMi4yMTYxNSAxMC4wNDU3IDEuOTYzNTQgMTAuMjAxOSAxLjc3MzQ0QzEwLjM1ODIgMS41ODA3MyAxMC41NDU3IDEuNDQyNzEgMTAuNzY0NCAxLjM1OTM4QzEwLjk4NTggMS4yNzYwNCAxMS4yMzMyIDEuMjM0MzggMTEuNTA2NiAxLjIzNDM4QzExLjcyNzkgMS4yMzQzOCAxMS45MzExIDEuMjYxNzIgMTIuMTE2IDEuMzE2NDFDMTIuMzAzNSAxLjM2ODQ5IDEyLjQ3MDEgMS40NTMxMiAxMi42MTYgMS41NzAzMUMxMi43NjE4IDEuNjg0OSAxMi44ODU1IDEuODM4NTQgMTIuOTg3MSAyLjAzMTI1QzEzLjA5MTIgMi4yMjEzNSAxMy4xNzA3IDIuNDU0NDMgMTMuMjI1MyAyLjczMDQ3QzEzLjI4IDMuMDA2NTEgMTMuMzA3NCAzLjMzMDczIDEzLjMwNzQgMy43MDMxMlpNMTIuNTgwOCA0LjY4NzVWMy41ODIwM0MxMi41ODA4IDMuMzI2ODIgMTIuNTY1MiAzLjEwMjg2IDEyLjUzMzkgMi45MTAxNkMxMi41MDUzIDIuNzE0ODQgMTIuNDYyMyAyLjU0ODE4IDEyLjQwNSAyLjQxMDE2QzEyLjM0NzcgMi4yNzIxNCAxMi4yNzQ4IDIuMTYwMTYgMTIuMTg2MyAyLjA3NDIyQzEyLjEwMDMgMS45ODgyOCAxMi4wMDAxIDEuOTI1NzggMTEuODg1NSAxLjg4NjcyQzExLjc3MzUgMS44NDUwNSAxMS42NDcyIDEuODI0MjIgMTEuNTA2NiAxLjgyNDIyQzExLjMzNDcgMS44MjQyMiAxMS4xODI0IDEuODU2NzcgMTEuMDQ5NiAxLjkyMTg4QzEwLjkxNjggMS45ODQzOCAxMC44MDQ4IDIuMDg0NjQgMTAuNzEzNiAyLjIyMjY2QzEwLjYyNTEgMi4zNjA2OCAxMC41NTc0IDIuNTQxNjcgMTAuNTEwNSAyLjc2NTYyQzEwLjQ2MzYgMi45ODk1OCAxMC40NDAyIDMuMjYxNzIgMTAuNDQwMiAzLjU4MjAzVjQuNjg3NUMxMC40NDAyIDQuOTQyNzEgMTAuNDU0NSA1LjE2Nzk3IDEwLjQ4MzIgNS4zNjMyOEMxMC41MTQ0IDUuNTU4NTkgMTAuNTYgNS43Mjc4NiAxMC42MTk5IDUuODcxMDlDMTAuNjc5OCA2LjAxMTcyIDEwLjc1MjcgNi4xMjc2IDEwLjgzODYgNi4yMTg3NUMxMC45MjQ2IDYuMzA5OSAxMS4wMjM1IDYuMzc3NiAxMS4xMzU1IDYuNDIxODhDMTEuMjUwMSA2LjQ2MzU0IDExLjM3NjQgNi40ODQzOCAxMS41MTQ0IDYuNDg0MzhDMTEuNjkxNSA2LjQ4NDM4IDExLjg0NjQgNi40NTA1MiAxMS45NzkzIDYuMzgyODFDMTIuMTEyMSA2LjMxNTEgMTIuMjIyNyA2LjIwOTY0IDEyLjMxMTMgNi4wNjY0MUMxMi40MDI0IDUuOTIwNTcgMTIuNDcwMSA1LjczNDM4IDEyLjUxNDQgNS41MDc4MUMxMi41NTg3IDUuMjc4NjUgMTIuNTgwOCA1LjAwNTIxIDEyLjU4MDggNC42ODc1Wk0xNC4zMDY4IDIuNzA3MDNWMi40MDYyNUMxNC4zMDY4IDIuMTkwMSAxNC4zNTM2IDEuOTkzNDkgMTQuNDQ3NCAxLjgxNjQxQzE0LjU0MTEgMS42MzkzMiAxNC42NzUzIDEuNDk3NCAxNC44NDk3IDEuMzkwNjJDMTUuMDI0MiAxLjI4Mzg1IDE1LjIzMTIgMS4yMzA0NyAxNS40NzA4IDEuMjMwNDdDMTUuNzE1NiAxLjIzMDQ3IDE1LjkyNCAxLjI4Mzg1IDE2LjA5NTggMS4zOTA2MkMxNi4yNzAzIDEuNDk3NCAxNi40MDQ0IDEuNjM5MzIgMTYuNDk4MiAxLjgxNjQxQzE2LjU5MTkgMS45OTM0OSAxNi42Mzg4IDIuMTkwMSAxNi42Mzg4IDIuNDA2MjVWMi43MDcwM0MxNi42Mzg4IDIuOTE3OTcgMTYuNTkxOSAzLjExMTk4IDE2LjQ5ODIgMy4yODkwNkMxNi40MDcgMy40NjYxNSAxNi4yNzQyIDMuNjA4MDcgMTYuMDk5NyAzLjcxNDg0QzE1LjkyNzkgMy44MjE2MSAxNS43MjA4IDMuODc1IDE1LjQ3ODYgMy44NzVDMTUuMjM2NSAzLjg3NSAxNS4wMjY4IDMuODIxNjEgMTQuODQ5NyAzLjcxNDg0QzE0LjY3NTMgMy42MDgwNyAxNC41NDExIDMuNDY2MTUgMTQuNDQ3NCAzLjI4OTA2QzE0LjM1MzYgMy4xMTE5OCAxNC4zMDY4IDIuOTE3OTcgMTQuMzA2OCAyLjcwNzAzWk0xNC44NDk3IDIuNDA2MjVWMi43MDcwM0MxNC44NDk3IDIuODI2ODIgMTQuODcxOSAyLjk0MDEgMTQuOTE2MSAzLjA0Njg4QzE0Ljk2MyAzLjE1MzY1IDE1LjAzMzMgMy4yNDA4OSAxNS4xMjcxIDMuMzA4NTlDMTUuMjIwOCAzLjM3MzcgMTUuMzM4IDMuNDA2MjUgMTUuNDc4NiAzLjQwNjI1QzE1LjYxOTMgMy40MDYyNSAxNS43MzUyIDMuMzczNyAxNS44MjYzIDMuMzA4NTlDMTUuOTE3NCAzLjI0MDg5IDE1Ljk4NTIgMy4xNTM2NSAxNi4wMjk0IDMuMDQ2ODhDMTYuMDczNyAyLjk0MDEgMTYuMDk1OCAyLjgyNjgyIDE2LjA5NTggMi43MDcwM1YyLjQwNjI1QzE2LjA5NTggMi4yODM4NSAxNi4wNzI0IDIuMTY5MjcgMTYuMDI1NSAyLjA2MjVDMTUuOTgxMiAxLjk1MzEyIDE1LjkxMjIgMS44NjU4OSAxNS44MTg1IDEuODAwNzhDMTUuNzI3MyAxLjczMzA3IDE1LjYxMTUgMS42OTkyMiAxNS40NzA4IDEuNjk5MjJDMTUuMzMyOCAxLjY5OTIyIDE1LjIxNjkgMS43MzMwNyAxNS4xMjMyIDEuODAwNzhDMTUuMDMyIDEuODY1ODkgMTQuOTYzIDEuOTUzMTIgMTQuOTE2MSAyLjA2MjVDMTQuODcxOSAyLjE2OTI3IDE0Ljg0OTcgMi4yODM4NSAxNC44NDk3IDIuNDA2MjVaTTE3LjA3NjMgNS45MTAxNlY1LjYwNTQ3QzE3LjA3NjMgNS4zOTE5MyAxNy4xMjMyIDUuMTk2NjEgMTcuMjE2OSA1LjAxOTUzQzE3LjMxMDcgNC44NDI0NSAxNy40NDQ4IDQuNzAwNTIgMTcuNjE5MyA0LjU5Mzc1QzE3Ljc5MzcgNC40ODY5OCAxOC4wMDA4IDQuNDMzNTkgMTguMjQwNCA0LjQzMzU5QzE4LjQ4NTIgNC40MzM1OSAxOC42OTM1IDQuNDg2OTggMTguODY1NCA0LjU5Mzc1QzE5LjAzOTggNC43MDA1MiAxOS4xNzQgNC44NDI0NSAxOS4yNjc3IDUuMDE5NTNDMTkuMzYxNSA1LjE5NjYxIDE5LjQwODMgNS4zOTE5MyAxOS40MDgzIDUuNjA1NDdWNS45MTAxNkMxOS40MDgzIDYuMTIzNyAxOS4zNjE1IDYuMzE5MDEgMTkuMjY3NyA2LjQ5NjA5QzE5LjE3NjYgNi42NzMxOCAxOS4wNDM3IDYuODE1MSAxOC44NjkzIDYuOTIxODhDMTguNjk3NCA3LjAyODY1IDE4LjQ5MDQgNy4wODIwMyAxOC4yNDgyIDcuMDgyMDNDMTguMDA2IDcuMDgyMDMgMTcuNzk3NyA3LjAyODY1IDE3LjYyMzIgNi45MjE4OEMxNy40NDg3IDYuODE1MSAxNy4zMTMzIDYuNjczMTggMTcuMjE2OSA2LjQ5NjA5QzE3LjEyMzIgNi4zMTkwMSAxNy4wNzYzIDYuMTIzNyAxNy4wNzYzIDUuOTEwMTZaTTE3LjYxOTMgNS42MDU0N1Y1LjkxMDE2QzE3LjYxOTMgNi4wMjk5NSAxNy42NDE0IDYuMTQ0NTMgMTcuNjg1NyA2LjI1MzkxQzE3LjczMjUgNi4zNjA2OCAxNy44MDI5IDYuNDQ3OTIgMTcuODk2NiA2LjUxNTYyQzE3Ljk5MDQgNi41ODA3MyAxOC4xMDc1IDYuNjEzMjggMTguMjQ4MiA2LjYxMzI4QzE4LjM4ODggNi42MTMyOCAxOC41MDQ3IDYuNTgwNzMgMTguNTk1OCA2LjUxNTYyQzE4LjY4OTYgNi40NDc5MiAxOC43NTg2IDYuMzYwNjggMTguODAyOSA2LjI1MzkxQzE4Ljg0NzEgNi4xNDcxNCAxOC44NjkzIDYuMDMyNTUgMTguODY5MyA1LjkxMDE2VjUuNjA1NDdDMTguODY5MyA1LjQ4MzA3IDE4Ljg0NTggNS4zNjg0OSAxOC43OTkgNS4yNjE3MkMxOC43NTQ3IDUuMTU0OTUgMTguNjg1NyA1LjA2OTAxIDE4LjU5MTkgNS4wMDM5MUMxOC41MDA4IDQuOTM2MiAxOC4zODM2IDQuOTAyMzQgMTguMjQwNCA0LjkwMjM0QzE4LjEwMjMgNC45MDIzNCAxNy45ODY1IDQuOTM2MiAxNy44OTI3IDUuMDAzOTFDMTcuODAxNiA1LjA2OTAxIDE3LjczMjUgNS4xNTQ5NSAxNy42ODU3IDUuMjYxNzJDMTcuNjQxNCA1LjM2ODQ5IDE3LjYxOTMgNS40ODMwNyAxNy42MTkzIDUuNjA1NDdaTTE4LjQyIDIuMTIxMDlMMTUuNjQyNyA2LjU2NjQxTDE1LjIzNjUgNi4zMDg1OUwxOC4wMTM4IDEuODYzMjhMMTguNDIgMi4xMjEwOVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPHBhdGggZD0iTTguMDU4NTkgMzMuNjY3N0M4LjA1ODU5IDM0LjAxNDEgNy45Nzc4NiAzNC4zMDgzIDcuODE2NDEgMzQuNTUwNUM3LjY1NzU1IDM0Ljc5MDEgNy40NDE0MSAzNC45NzI0IDcuMTY3OTcgMzUuMDk3NEM2Ljg5NzE0IDM1LjIyMjQgNi41OTExNSAzNS4yODQ5IDYuMjUgMzUuMjg0OUM1LjkwODg1IDM1LjI4NDkgNS42MDE1NiAzNS4yMjI0IDUuMzI4MTIgMzUuMDk3NEM1LjA1NDY5IDM0Ljk3MjQgNC44Mzg1NCAzNC43OTAxIDQuNjc5NjkgMzQuNTUwNUM0LjUyMDgzIDM0LjMwODMgNC40NDE0MSAzNC4wMTQxIDQuNDQxNDEgMzMuNjY3N0M0LjQ0MTQxIDMzLjQ0MTIgNC40ODQzOCAzMy4yMzQxIDQuNTcwMzEgMzMuMDQ2NkM0LjY1ODg1IDMyLjg1NjUgNC43ODI1NSAzMi42OTEyIDQuOTQxNDEgMzIuNTUwNUM1LjEwMjg2IDMyLjQwOTkgNS4yOTI5NyAzMi4zMDE4IDUuNTExNzIgMzIuMjI2M0M1LjczMzA3IDMyLjE0ODIgNS45NzY1NiAzMi4xMDkxIDYuMjQyMTkgMzIuMTA5MUM2LjU5MTE1IDMyLjEwOTEgNi45MDIzNCAzMi4xNzY4IDcuMTc1NzggMzIuMzEyM0M3LjQ0OTIyIDMyLjQ0NTEgNy42NjQwNiAzMi42Mjg3IDcuODIwMzEgMzIuODYzQzcuOTc5MTcgMzMuMDk3NCA4LjA1ODU5IDMzLjM2NTYgOC4wNTg1OSAzMy42Njc3Wk03LjMzMjAzIDMzLjY1MjFDNy4zMzIwMyAzMy40NDEyIDcuMjg2NDYgMzMuMjU1IDcuMTk1MzEgMzMuMDkzNUM3LjEwNDE3IDMyLjkyOTQgNi45NzY1NiAzMi44MDE4IDYuODEyNSAzMi43MTA3QzYuNjQ4NDQgMzIuNjE5NSA2LjQ1ODMzIDMyLjU3NCA2LjI0MjE5IDMyLjU3NEM2LjAyMDgzIDMyLjU3NCA1LjgyOTQzIDMyLjYxOTUgNS42Njc5NyAzMi43MTA3QzUuNTA5MTEgMzIuODAxOCA1LjM4NTQyIDMyLjkyOTQgNS4yOTY4OCAzMy4wOTM1QzUuMjA4MzMgMzMuMjU1IDUuMTY0MDYgMzMuNDQxMiA1LjE2NDA2IDMzLjY1MjFDNS4xNjQwNiAzMy44NzA4IDUuMjA3MDMgMzQuMDU4MyA1LjI5Mjk3IDM0LjIxNDZDNS4zODE1MSAzNC4zNjgyIDUuNTA2NTEgMzQuNDg2NyA1LjY2Nzk3IDM0LjU3MDFDNS44MzIwMyAzNC42NTA4IDYuMDI2MDQgMzQuNjkxMiA2LjI1IDM0LjY5MTJDNi40NzM5NiAzNC42OTEyIDYuNjY2NjcgMzQuNjUwOCA2LjgyODEyIDM0LjU3MDFDNi45ODk1OCAzNC40ODY3IDcuMTEzMjggMzQuMzY4MiA3LjE5OTIyIDM0LjIxNDZDNy4yODc3NiAzNC4wNTgzIDcuMzMyMDMgMzMuODcwOCA3LjMzMjAzIDMzLjY1MjFaTTcuOTI1NzggMzAuOTk5OEM3LjkyNTc4IDMxLjI3NTggNy44NTI4NiAzMS41MjQ1IDcuNzA3MDMgMzEuNzQ1OEM3LjU2MTIgMzEuOTY3MiA3LjM2MTk4IDMyLjE0MTcgNy4xMDkzOCAzMi4yNjkzQzYuODU2NzcgMzIuMzk2OSA2LjU3MDMxIDMyLjQ2MDcgNi4yNSAzMi40NjA3QzUuOTI0NDggMzIuNDYwNyA1LjYzNDExIDMyLjM5NjkgNS4zNzg5MSAzMi4yNjkzQzUuMTI2MyAzMi4xNDE3IDQuOTI4MzkgMzEuOTY3MiA0Ljc4NTE2IDMxLjc0NThDNC42NDE5MyAzMS41MjQ1IDQuNTcwMzEgMzEuMjc1OCA0LjU3MDMxIDMwLjk5OThDNC41NzAzMSAzMC42NjkgNC42NDE5MyAzMC4zODc4IDQuNzg1MTYgMzAuMTU2QzQuOTMwOTkgMjkuOTI0MiA1LjEzMDIxIDI5Ljc0NzIgNS4zODI4MSAyOS42MjQ4QzUuNjM1NDIgMjkuNTAyNCA1LjkyMzE4IDI5LjQ0MTIgNi4yNDYwOSAyOS40NDEyQzYuNTcxNjEgMjkuNDQxMiA2Ljg2MDY4IDI5LjUwMjQgNy4xMTMyOCAyOS42MjQ4QzcuMzY1ODkgMjkuNzQ3MiA3LjU2MzggMjkuOTI0MiA3LjcwNzAzIDMwLjE1NkM3Ljg1Mjg2IDMwLjM4NzggNy45MjU3OCAzMC42NjkgNy45MjU3OCAzMC45OTk4Wk03LjIwMzEyIDMxLjAxMTVDNy4yMDMxMiAzMC44MjE0IDcuMTYyNzYgMzAuNjUzNCA3LjA4MjAzIDMwLjUwNzZDNy4wMDEzIDMwLjM2MTcgNi44ODkzMiAzMC4yNDcyIDYuNzQ2MDkgMzAuMTYzOEM2LjYwMjg2IDMwLjA3NzkgNi40MzYyIDMwLjAzNDkgNi4yNDYwOSAzMC4wMzQ5QzYuMDU1OTkgMzAuMDM0OSA1Ljg4OTMyIDMwLjA3NTMgNS43NDYwOSAzMC4xNTZDNS42MDU0NyAzMC4yMzQxIDUuNDk0NzkgMzAuMzQ2MSA1LjQxNDA2IDMwLjQ5MTlDNS4zMzU5NCAzMC42Mzc4IDUuMjk2ODggMzAuODExIDUuMjk2ODggMzEuMDExNUM1LjI5Njg4IDMxLjIwNjggNS4zMzU5NCAzMS4zNzc0IDUuNDE0MDYgMzEuNTIzMkM1LjQ5NDc5IDMxLjY2OSA1LjYwNjc3IDMxLjc4MjMgNS43NSAzMS44NjNDNS44OTMyMyAzMS45NDM4IDYuMDU5OSAzMS45ODQxIDYuMjUgMzEuOTg0MUM2LjQ0MDEgMzEuOTg0MSA2LjYwNTQ3IDMxLjk0MzggNi43NDYwOSAzMS44NjNDNi44ODkzMiAzMS43ODIzIDcuMDAxMyAzMS42NjkgNy4wODIwMyAzMS41MjMyQzcuMTYyNzYgMzEuMzc3NCA3LjIwMzEyIDMxLjIwNjggNy4yMDMxMiAzMS4wMTE1Wk0xMi42NzUyIDMxLjkwOTlWMzIuNzc3MUMxMi42NzUyIDMzLjI0MzIgMTIuNjMzNSAzMy42MzY1IDEyLjU1MDIgMzMuOTU2OEMxMi40NjY4IDM0LjI3NzEgMTIuMzQ3IDM0LjUzNDkgMTIuMTkwOCAzNC43MzAyQzEyLjAzNDUgMzQuOTI1NSAxMS44NDU3IDM1LjA2NzUgMTEuNjI0NCAzNS4xNTZDMTEuNDA1NiAzNS4yNDE5IDExLjE1ODIgMzUuMjg0OSAxMC44ODIyIDM1LjI4NDlDMTAuNjYzNSAzNS4yODQ5IDEwLjQ2MTYgMzUuMjU3NiAxMC4yNzY3IDM1LjIwMjlDMTAuMDkxOCAzNS4xNDgyIDkuOTI1MTcgMzUuMDYxIDkuNzc2NzMgMzQuOTQxMkM5LjYzMDkgMzQuODE4OCA5LjUwNTkgMzQuNjU5OSA5LjQwMTczIDM0LjQ2NDZDOS4yOTc1NyAzNC4yNjkzIDkuMjE4MTQgMzQuMDMyMyA5LjE2MzQ1IDMzLjc1MzdDOS4xMDg3NyAzMy40NzUgOS4wODE0MiAzMy4xNDk1IDkuMDgxNDIgMzIuNzc3MVYzMS45MDk5QzkuMDgxNDIgMzEuNDQzOCA5LjEyMzA5IDMxLjA1MzEgOS4yMDY0MiAzMC43MzhDOS4yOTIzNiAzMC40MjI5IDkuNDEzNDUgMzAuMTcwMyA5LjU2OTcgMjkuOTgwMkM5LjcyNTk1IDI5Ljc4NzUgOS45MTM0NSAyOS42NDk1IDEwLjEzMjIgMjkuNTY2MkMxMC4zNTM2IDI5LjQ4MjggMTAuNjAxIDI5LjQ0MTIgMTAuODc0NCAyOS40NDEyQzExLjA5NTcgMjkuNDQxMiAxMS4yOTg5IDI5LjQ2ODUgMTEuNDgzOCAyOS41MjMyQzExLjY3MTMgMjkuNTc1MyAxMS44Mzc5IDI5LjY1OTkgMTEuOTgzOCAyOS43NzcxQzEyLjEyOTYgMjkuODkxNyAxMi4yNTMzIDMwLjA0NTMgMTIuMzU0OSAzMC4yMzhDMTIuNDU5IDMwLjQyODEgMTIuNTM4NSAzMC42NjEyIDEyLjU5MzEgMzAuOTM3M0MxMi42NDc4IDMxLjIxMzMgMTIuNjc1MiAzMS41Mzc1IDEyLjY3NTIgMzEuOTA5OVpNMTEuOTQ4NiAzMi44OTQzVjMxLjc4ODhDMTEuOTQ4NiAzMS41MzM2IDExLjkzMyAzMS4zMDk3IDExLjkwMTcgMzEuMTE2OUMxMS44NzMxIDMwLjkyMTYgMTEuODMwMSAzMC43NTUgMTEuNzcyOCAzMC42MTY5QzExLjcxNTUgMzAuNDc4OSAxMS42NDI2IDMwLjM2NjkgMTEuNTU0MSAzMC4yODFDMTEuNDY4MSAzMC4xOTUxIDExLjM2NzkgMzAuMTMyNiAxMS4yNTMzIDMwLjA5MzVDMTEuMTQxMyAzMC4wNTE4IDExLjAxNSAzMC4wMzEgMTAuODc0NCAzMC4wMzFDMTAuNzAyNSAzMC4wMzEgMTAuNTUwMiAzMC4wNjM2IDEwLjQxNzQgMzAuMTI4N0MxMC4yODQ1IDMwLjE5MTIgMTAuMTcyNiAzMC4yOTE0IDEwLjA4MTQgMzAuNDI5NEM5Ljk5Mjg4IDMwLjU2NzUgOS45MjUxNyAzMC43NDg1IDkuODc4MyAzMC45NzI0QzkuODMxNDIgMzEuMTk2NCA5LjgwNzk4IDMxLjQ2ODUgOS44MDc5OCAzMS43ODg4VjMyLjg5NDNDOS44MDc5OCAzMy4xNDk1IDkuODIyMzEgMzMuMzc0OCA5Ljg1MDk1IDMzLjU3MDFDOS44ODIyIDMzLjc2NTQgOS45Mjc3OCAzMy45MzQ3IDkuOTg3NjcgMzQuMDc3OUMxMC4wNDc2IDM0LjIxODUgMTAuMTIwNSAzNC4zMzQ0IDEwLjIwNjQgMzQuNDI1NUMxMC4yOTI0IDM0LjUxNjcgMTAuMzkxMyAzNC41ODQ0IDEwLjUwMzMgMzQuNjI4N0MxMC42MTc5IDM0LjY3MDMgMTAuNzQ0MiAzNC42OTEyIDEwLjg4MjIgMzQuNjkxMkMxMS4wNTkzIDM0LjY5MTIgMTEuMjE0MiAzNC42NTczIDExLjM0NyAzNC41ODk2QzExLjQ3OTkgMzQuNTIxOSAxMS41OTA1IDM0LjQxNjQgMTEuNjc5MSAzNC4yNzMyQzExLjc3MDIgMzQuMTI3NCAxMS44Mzc5IDMzLjk0MTIgMTEuODgyMiAzMy43MTQ2QzExLjkyNjUgMzMuNDg1NCAxMS45NDg2IDMzLjIxMiAxMS45NDg2IDMyLjg5NDNaTTEzLjY3NDYgMzAuOTEzOFYzMC42MTNDMTMuNjc0NiAzMC4zOTY5IDEzLjcyMTQgMzAuMjAwMyAxMy44MTUyIDMwLjAyMzJDMTMuOTA4OSAyOS44NDYxIDE0LjA0MzEgMjkuNzA0MiAxNC4yMTc1IDI5LjU5NzRDMTQuMzkyIDI5LjQ5MDYgMTQuNTk5IDI5LjQzNzMgMTQuODM4NiAyOS40MzczQzE1LjA4MzQgMjkuNDM3MyAxNS4yOTE4IDI5LjQ5MDYgMTUuNDYzNiAyOS41OTc0QzE1LjYzODEgMjkuNzA0MiAxNS43NzIyIDI5Ljg0NjEgMTUuODY2IDMwLjAyMzJDMTUuOTU5NyAzMC4yMDAzIDE2LjAwNjYgMzAuMzk2OSAxNi4wMDY2IDMwLjYxM1YzMC45MTM4QzE2LjAwNjYgMzEuMTI0OCAxNS45NTk3IDMxLjMxODggMTUuODY2IDMxLjQ5NThDMTUuNzc0OCAzMS42NzI5IDE1LjY0MiAzMS44MTQ5IDE1LjQ2NzUgMzEuOTIxNkMxNS4yOTU3IDMyLjAyODQgMTUuMDg4NiAzMi4wODE4IDE0Ljg0NjQgMzIuMDgxOEMxNC42MDQzIDMyLjA4MTggMTQuMzk0NiAzMi4wMjg0IDE0LjIxNzUgMzEuOTIxNkMxNC4wNDMxIDMxLjgxNDkgMTMuOTA4OSAzMS42NzI5IDEzLjgxNTIgMzEuNDk1OEMxMy43MjE0IDMxLjMxODggMTMuNjc0NiAzMS4xMjQ4IDEzLjY3NDYgMzAuOTEzOFpNMTQuMjE3NSAzMC42MTNWMzAuOTEzOEMxNC4yMTc1IDMxLjAzMzYgMTQuMjM5NyAzMS4xNDY5IDE0LjI4MzkgMzEuMjUzN0MxNC4zMzA4IDMxLjM2MDQgMTQuNDAxMSAzMS40NDc3IDE0LjQ5NDkgMzEuNTE1NEMxNC41ODg2IDMxLjU4MDUgMTQuNzA1OCAzMS42MTMgMTQuODQ2NCAzMS42MTNDMTQuOTg3MSAzMS42MTMgMTUuMTAyOSAzMS41ODA1IDE1LjE5NDEgMzEuNTE1NEMxNS4yODUyIDMxLjQ0NzcgMTUuMzUyOSAzMS4zNjA0IDE1LjM5NzIgMzEuMjUzN0MxNS40NDE1IDMxLjE0NjkgMTUuNDYzNiAzMS4wMzM2IDE1LjQ2MzYgMzAuOTEzOFYzMC42MTNDMTUuNDYzNiAzMC40OTA2IDE1LjQ0MDIgMzAuMzc2MSAxNS4zOTMzIDMwLjI2OTNDMTUuMzQ5IDMwLjE1OTkgMTUuMjggMzAuMDcyNyAxNS4xODYzIDMwLjAwNzZDMTUuMDk1MSAyOS45Mzk5IDE0Ljk3OTMgMjkuOTA2IDE0LjgzODYgMjkuOTA2QzE0LjcwMDYgMjkuOTA2IDE0LjU4NDcgMjkuOTM5OSAxNC40OTEgMzAuMDA3NkMxNC4zOTk4IDMwLjA3MjcgMTQuMzMwOCAzMC4xNTk5IDE0LjI4MzkgMzAuMjY5M0MxNC4yMzk3IDMwLjM3NjEgMTQuMjE3NSAzMC40OTA2IDE0LjIxNzUgMzAuNjEzWk0xNi40NDQxIDM0LjExNjlWMzMuODEyM0MxNi40NDQxIDMzLjU5ODcgMTYuNDkxIDMzLjQwMzQgMTYuNTg0NyAzMy4yMjYzQzE2LjY3ODUgMzMuMDQ5MiAxNi44MTI2IDMyLjkwNzMgMTYuOTg3MSAzMi44MDA1QzE3LjE2MTUgMzIuNjkzOCAxNy4zNjg2IDMyLjY0MDQgMTcuNjA4MiAzMi42NDA0QzE3Ljg1MjkgMzIuNjQwNCAxOC4wNjEzIDMyLjY5MzggMTguMjMzMiAzMi44MDA1QzE4LjQwNzYgMzIuOTA3MyAxOC41NDE4IDMzLjA0OTIgMTguNjM1NSAzMy4yMjYzQzE4LjcyOTMgMzMuNDAzNCAxOC43NzYxIDMzLjU5ODcgMTguNzc2MSAzMy44MTIzVjM0LjExNjlDMTguNzc2MSAzNC4zMzA1IDE4LjcyOTMgMzQuNTI1OCAxOC42MzU1IDM0LjcwMjlDMTguNTQ0NCAzNC44OCAxOC40MTE1IDM1LjAyMTkgMTguMjM3MSAzNS4xMjg3QzE4LjA2NTIgMzUuMjM1NCAxNy44NTgyIDM1LjI4ODggMTcuNjE2IDM1LjI4ODhDMTcuMzczOCAzNS4yODg4IDE3LjE2NTQgMzUuMjM1NCAxNi45OTEgMzUuMTI4N0MxNi44MTY1IDM1LjAyMTkgMTYuNjgxMSAzNC44OCAxNi41ODQ3IDM0LjcwMjlDMTYuNDkxIDM0LjUyNTggMTYuNDQ0MSAzNC4zMzA1IDE2LjQ0NDEgMzQuMTE2OVpNMTYuOTg3MSAzMy44MTIzVjM0LjExNjlDMTYuOTg3MSAzNC4yMzY3IDE3LjAwOTIgMzQuMzUxMyAxNy4wNTM1IDM0LjQ2MDdDMTcuMTAwMyAzNC41Njc1IDE3LjE3MDcgMzQuNjU0NyAxNy4yNjQ0IDM0LjcyMjRDMTcuMzU4MiAzNC43ODc1IDE3LjQ3NTMgMzQuODIwMSAxNy42MTYgMzQuODIwMUMxNy43NTY2IDM0LjgyMDEgMTcuODcyNSAzNC43ODc1IDE3Ljk2MzYgMzQuNzIyNEMxOC4wNTc0IDM0LjY1NDcgMTguMTI2NCAzNC41Njc1IDE4LjE3MDcgMzQuNDYwN0MxOC4yMTQ5IDM0LjM1MzkgMTguMjM3MSAzNC4yMzkzIDE4LjIzNzEgMzQuMTE2OVYzMy44MTIzQzE4LjIzNzEgMzMuNjg5OSAxOC4yMTM2IDMzLjU3NTMgMTguMTY2OCAzMy40Njg1QzE4LjEyMjUgMzMuMzYxNyAxOC4wNTM1IDMzLjI3NTggMTcuOTU5NyAzMy4yMTA3QzE3Ljg2ODYgMzMuMTQzIDE3Ljc1MTQgMzMuMTA5MSAxNy42MDgyIDMzLjEwOTFDMTcuNDcwMSAzMy4xMDkxIDE3LjM1NDMgMzMuMTQzIDE3LjI2MDUgMzMuMjEwN0MxNy4xNjk0IDMzLjI3NTggMTcuMTAwMyAzMy4zNjE3IDE3LjA1MzUgMzMuNDY4NUMxNy4wMDkyIDMzLjU3NTMgMTYuOTg3MSAzMy42ODk5IDE2Ljk4NzEgMzMuODEyM1pNMTcuNzg3OCAzMC4zMjc5TDE1LjAxMDUgMzQuNzczMkwxNC42MDQzIDM0LjUxNTRMMTcuMzgxNiAzMC4wNzAxTDE3Ljc4NzggMzAuMzI3OVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPHBhdGggZD0iTTcuMjQ2MDkgNTcuNzE4M0g3LjMwODU5VjU4LjMzMTVINy4yNDYwOUM2Ljg2MzI4IDU4LjMzMTUgNi41NDI5NyA1OC4zOTQgNi4yODUxNiA1OC41MTlDNi4wMjczNCA1OC42NDE0IDUuODIyOTIgNTguODA2OCA1LjY3MTg4IDU5LjAxNTFDNS41MjA4MyA1OS4yMjA5IDUuNDExNDYgNTkuNDUyNiA1LjM0Mzc1IDU5LjcxMDRDNS4yNzg2NSA1OS45NjgzIDUuMjQ2MDkgNjAuMjMgNS4yNDYwOSA2MC40OTU2VjYxLjMzMTVDNS4yNDYwOSA2MS41ODQxIDUuMjc2MDQgNjEuODA4MSA1LjMzNTk0IDYyLjAwMzRDNS4zOTU4MyA2Mi4xOTYxIDUuNDc3ODYgNjIuMzU4OSA1LjU4MjAzIDYyLjQ5MTdDNS42ODYyIDYyLjYyNDUgNS44MDMzOSA2Mi43MjQ4IDUuOTMzNTkgNjIuNzkyNUM2LjA2NjQxIDYyLjg2MDIgNi4yMDQ0MyA2Mi44OTQgNi4zNDc2NiA2Mi44OTRDNi41MTQzMiA2Mi44OTQgNi42NjI3NiA2Mi44NjI4IDYuNzkyOTcgNjIuODAwM0M2LjkyMzE4IDYyLjczNTIgNy4wMzI1NSA2Mi42NDUzIDcuMTIxMDkgNjIuNTMwOEM3LjIxMjI0IDYyLjQxMzYgNy4yODEyNSA2Mi4yNzU2IDcuMzI4MTIgNjIuMTE2N0M3LjM3NSA2MS45NTc4IDcuMzk4NDQgNjEuNzgzNCA3LjM5ODQ0IDYxLjU5MzNDNy4zOTg0NCA2MS40MjQgNy4zNzc2IDYxLjI2MTIgNy4zMzU5NCA2MS4xMDVDNy4yOTQyNyA2MC45NDYxIDcuMjMwNDcgNjAuODA1NSA3LjE0NDUzIDYwLjY4MzFDNy4wNTg1OSA2MC41NTgxIDYuOTUwNTIgNjAuNDYwNCA2LjgyMDMxIDYwLjM5MDFDNi42OTI3MSA2MC4zMTcyIDYuNTQwMzYgNjAuMjgwOCA2LjM2MzI4IDYwLjI4MDhDNi4xNjI3NiA2MC4yODA4IDUuOTc1MjYgNjAuMzMwMiA1LjgwMDc4IDYwLjQyOTJDNS42Mjg5MSA2MC41MjU2IDUuNDg2OTggNjAuNjUzMiA1LjM3NSA2MC44MTJDNS4yNjU2MiA2MC45NjgzIDUuMjAzMTIgNjEuMTM4OCA1LjE4NzUgNjEuMzIzN0w0LjgwNDY5IDYxLjMxOThDNC44NDExNSA2MS4wMjgyIDQuOTA4ODUgNjAuNzc5NSA1LjAwNzgxIDYwLjU3MzdDNS4xMDkzOCA2MC4zNjU0IDUuMjM0MzggNjAuMTk2MSA1LjM4MjgxIDYwLjA2NTlDNS41MzM4NSA1OS45MzMxIDUuNzAxODIgNTkuODM2OCA1Ljg4NjcyIDU5Ljc3NjlDNi4wNzQyMiA1OS43MTQ0IDYuMjcyMTQgNTkuNjgzMSA2LjQ4MDQ3IDU5LjY4MzFDNi43NjQzMiA1OS42ODMxIDcuMDA5MTEgNTkuNzM2NSA3LjIxNDg0IDU5Ljg0MzNDNy40MjA1NyA1OS45NSA3LjU4OTg0IDYwLjA5MzMgNy43MjI2NiA2MC4yNzI5QzcuODU1NDcgNjAuNDUgNy45NTMxMiA2MC42NTA2IDguMDE1NjIgNjAuODc0NUM4LjA4MDczIDYxLjA5NTkgOC4xMTMyOCA2MS4zMjM3IDguMTEzMjggNjEuNTU4MUM4LjExMzI4IDYxLjgyNjMgOC4wNzU1MiA2Mi4wNzc2IDggNjIuMzEyQzcuOTI0NDggNjIuNTQ2NCA3LjgxMTIgNjIuNzUyMSA3LjY2MDE2IDYyLjkyOTJDNy41MTE3MiA2My4xMDYzIDcuMzI4MTIgNjMuMjQ0MyA3LjEwOTM4IDYzLjM0MzNDNi44OTA2MiA2My40NDIyIDYuNjM2NzIgNjMuNDkxNyA2LjM0NzY2IDYzLjQ5MTdDNi4wNDAzNiA2My40OTE3IDUuNzcyMTQgNjMuNDI5MiA1LjU0Mjk3IDYzLjMwNDJDNS4zMTM4IDYzLjE3NjYgNS4xMjM3IDYzLjAwNzMgNC45NzI2NiA2Mi43OTY0QzQuODIxNjEgNjIuNTg1NCA0LjcwODMzIDYyLjM1MTEgNC42MzI4MSA2Mi4wOTMzQzQuNTU3MjkgNjEuODM1NCA0LjUxOTUzIDYxLjU3MzcgNC41MTk1MyA2MS4zMDgxVjYwLjk2ODNDNC41MTk1MyA2MC41NjcyIDQuNTU5OSA2MC4xNzQgNC42NDA2MiA1OS43ODg2QzQuNzIxMzUgNTkuNDAzMiA0Ljg2MDY4IDU5LjA1NDIgNS4wNTg1OSA1OC43NDE3QzUuMjU5MTEgNTguNDI5MiA1LjUzNjQ2IDU4LjE4MDUgNS44OTA2MiA1Ny45OTU2QzYuMjQ0NzkgNTcuODEwNyA2LjY5NjYxIDU3LjcxODMgNy4yNDYwOSA1Ny43MTgzWk0xMi42NzUyIDYwLjExNjdWNjAuOTgzOUMxMi42NzUyIDYxLjQ1IDEyLjYzMzUgNjEuODQzMyAxMi41NTAyIDYyLjE2MzZDMTIuNDY2OCA2Mi40ODM5IDEyLjM0NyA2Mi43NDE3IDEyLjE5MDggNjIuOTM3QzEyLjAzNDUgNjMuMTMyMyAxMS44NDU3IDYzLjI3NDMgMTEuNjI0NCA2My4zNjI4QzExLjQwNTYgNjMuNDQ4NyAxMS4xNTgyIDYzLjQ5MTcgMTAuODgyMiA2My40OTE3QzEwLjY2MzUgNjMuNDkxNyAxMC40NjE2IDYzLjQ2NDQgMTAuMjc2NyA2My40MDk3QzEwLjA5MTggNjMuMzU1IDkuOTI1MTcgNjMuMjY3NyA5Ljc3NjczIDYzLjE0NzlDOS42MzA5IDYzLjAyNTYgOS41MDU5IDYyLjg2NjcgOS40MDE3MyA2Mi42NzE0QzkuMjk3NTcgNjIuNDc2MSA5LjIxODE0IDYyLjIzOTEgOS4xNjM0NSA2MS45NjA0QzkuMTA4NzcgNjEuNjgxOCA5LjA4MTQyIDYxLjM1NjMgOS4wODE0MiA2MC45ODM5VjYwLjExNjdDOS4wODE0MiA1OS42NTA2IDkuMTIzMDkgNTkuMjU5OSA5LjIwNjQyIDU4Ljk0NDhDOS4yOTIzNiA1OC42Mjk3IDkuNDEzNDUgNTguMzc3MSA5LjU2OTcgNTguMTg3QzkuNzI1OTUgNTcuOTk0MyA5LjkxMzQ1IDU3Ljg1NjMgMTAuMTMyMiA1Ny43NzI5QzEwLjM1MzYgNTcuNjg5NiAxMC42MDEgNTcuNjQ3OSAxMC44NzQ0IDU3LjY0NzlDMTEuMDk1NyA1Ny42NDc5IDExLjI5ODkgNTcuNjc1MyAxMS40ODM4IDU3LjczQzExLjY3MTMgNTcuNzgyMSAxMS44Mzc5IDU3Ljg2NjcgMTEuOTgzOCA1Ny45ODM5QzEyLjEyOTYgNTguMDk4NSAxMi4yNTMzIDU4LjI1MjEgMTIuMzU0OSA1OC40NDQ4QzEyLjQ1OSA1OC42MzQ5IDEyLjUzODUgNTguODY4IDEyLjU5MzEgNTkuMTQ0QzEyLjY0NzggNTkuNDIwMSAxMi42NzUyIDU5Ljc0NDMgMTIuNjc1MiA2MC4xMTY3Wk0xMS45NDg2IDYxLjEwMTFWNTkuOTk1NkMxMS45NDg2IDU5Ljc0MDQgMTEuOTMzIDU5LjUxNjQgMTEuOTAxNyA1OS4zMjM3QzExLjg3MzEgNTkuMTI4NCAxMS44MzAxIDU4Ljk2MTggMTEuNzcyOCA1OC44MjM3QzExLjcxNTUgNTguNjg1NyAxMS42NDI2IDU4LjU3MzcgMTEuNTU0MSA1OC40ODc4QzExLjQ2ODEgNTguNDAxOSAxMS4zNjc5IDU4LjMzOTQgMTEuMjUzMyA1OC4zMDAzQzExLjE0MTMgNTguMjU4NiAxMS4wMTUgNTguMjM3OCAxMC44NzQ0IDU4LjIzNzhDMTAuNzAyNSA1OC4yMzc4IDEwLjU1MDIgNTguMjcwMyAxMC40MTc0IDU4LjMzNTRDMTAuMjg0NSA1OC4zOTc5IDEwLjE3MjYgNTguNDk4MiAxMC4wODE0IDU4LjYzNjJDOS45OTI4OCA1OC43NzQzIDkuOTI1MTcgNTguOTU1MiA5Ljg3ODMgNTkuMTc5MkM5LjgzMTQyIDU5LjQwMzIgOS44MDc5OCA1OS42NzUzIDkuODA3OTggNTkuOTk1NlY2MS4xMDExQzkuODA3OTggNjEuMzU2MyA5LjgyMjMxIDYxLjU4MTUgOS44NTA5NSA2MS43NzY5QzkuODgyMiA2MS45NzIyIDkuOTI3NzggNjIuMTQxNCA5Ljk4NzY3IDYyLjI4NDdDMTAuMDQ3NiA2Mi40MjUzIDEwLjEyMDUgNjIuNTQxMiAxMC4yMDY0IDYyLjYzMjNDMTAuMjkyNCA2Mi43MjM1IDEwLjM5MTMgNjIuNzkxMiAxMC41MDMzIDYyLjgzNTRDMTAuNjE3OSA2Mi44NzcxIDEwLjc0NDIgNjIuODk3OSAxMC44ODIyIDYyLjg5NzlDMTEuMDU5MyA2Mi44OTc5IDExLjIxNDIgNjIuODY0MSAxMS4zNDcgNjIuNzk2NEMxMS40Nzk5IDYyLjcyODcgMTEuNTkwNSA2Mi42MjMyIDExLjY3OTEgNjIuNDhDMTEuNzcwMiA2Mi4zMzQxIDExLjgzNzkgNjIuMTQ3OSAxMS44ODIyIDYxLjkyMTRDMTEuOTI2NSA2MS42OTIyIDExLjk0ODYgNjEuNDE4OCAxMS45NDg2IDYxLjEwMTFaTTEzLjY3NDYgNTkuMTIwNlY1OC44MTk4QzEzLjY3NDYgNTguNjAzNyAxMy43MjE0IDU4LjQwNzEgMTMuODE1MiA1OC4yM0MxMy45MDg5IDU4LjA1MjkgMTQuMDQzMSA1Ny45MTEgMTQuMjE3NSA1Ny44MDQyQzE0LjM5MiA1Ny42OTc0IDE0LjU5OSA1Ny42NDQgMTQuODM4NiA1Ny42NDRDMTUuMDgzNCA1Ny42NDQgMTUuMjkxOCA1Ny42OTc0IDE1LjQ2MzYgNTcuODA0MkMxNS42MzgxIDU3LjkxMSAxNS43NzIyIDU4LjA1MjkgMTUuODY2IDU4LjIzQzE1Ljk1OTcgNTguNDA3MSAxNi4wMDY2IDU4LjYwMzcgMTYuMDA2NiA1OC44MTk4VjU5LjEyMDZDMTYuMDA2NiA1OS4zMzE1IDE1Ljk1OTcgNTkuNTI1NiAxNS44NjYgNTkuNzAyNkMxNS43NzQ4IDU5Ljg3OTcgMTUuNjQyIDYwLjAyMTYgMTUuNDY3NSA2MC4xMjg0QzE1LjI5NTcgNjAuMjM1MiAxNS4wODg2IDYwLjI4ODYgMTQuODQ2NCA2MC4yODg2QzE0LjYwNDMgNjAuMjg4NiAxNC4zOTQ2IDYwLjIzNTIgMTQuMjE3NSA2MC4xMjg0QzE0LjA0MzEgNjAuMDIxNiAxMy45MDg5IDU5Ljg3OTcgMTMuODE1MiA1OS43MDI2QzEzLjcyMTQgNTkuNTI1NiAxMy42NzQ2IDU5LjMzMTUgMTMuNjc0NiA1OS4xMjA2Wk0xNC4yMTc1IDU4LjgxOThWNTkuMTIwNkMxNC4yMTc1IDU5LjI0MDQgMTQuMjM5NyA1OS4zNTM3IDE0LjI4MzkgNTkuNDYwNEMxNC4zMzA4IDU5LjU2NzIgMTQuNDAxMSA1OS42NTQ1IDE0LjQ5NDkgNTkuNzIyMkMxNC41ODg2IDU5Ljc4NzMgMTQuNzA1OCA1OS44MTk4IDE0Ljg0NjQgNTkuODE5OEMxNC45ODcxIDU5LjgxOTggMTUuMTAyOSA1OS43ODczIDE1LjE5NDEgNTkuNzIyMkMxNS4yODUyIDU5LjY1NDUgMTUuMzUyOSA1OS41NjcyIDE1LjM5NzIgNTkuNDYwNEMxNS40NDE1IDU5LjM1MzcgMTUuNDYzNiA1OS4yNDA0IDE1LjQ2MzYgNTkuMTIwNlY1OC44MTk4QzE1LjQ2MzYgNTguNjk3NCAxNS40NDAyIDU4LjU4MjggMTUuMzkzMyA1OC40NzYxQzE1LjM0OSA1OC4zNjY3IDE1LjI4IDU4LjI3OTUgMTUuMTg2MyA1OC4yMTQ0QzE1LjA5NTEgNTguMTQ2NiAxNC45NzkzIDU4LjExMjggMTQuODM4NiA1OC4xMTI4QzE0LjcwMDYgNTguMTEyOCAxNC41ODQ3IDU4LjE0NjYgMTQuNDkxIDU4LjIxNDRDMTQuMzk5OCA1OC4yNzk1IDE0LjMzMDggNTguMzY2NyAxNC4yODM5IDU4LjQ3NjFDMTQuMjM5NyA1OC41ODI4IDE0LjIxNzUgNTguNjk3NCAxNC4yMTc1IDU4LjgxOThaTTE2LjQ0NDEgNjIuMzIzN1Y2Mi4wMTlDMTYuNDQ0MSA2MS44MDU1IDE2LjQ5MSA2MS42MTAyIDE2LjU4NDcgNjEuNDMzMUMxNi42Nzg1IDYxLjI1NiAxNi44MTI2IDYxLjExNDEgMTYuOTg3MSA2MS4wMDczQzE3LjE2MTUgNjAuOTAwNiAxNy4zNjg2IDYwLjg0NzIgMTcuNjA4MiA2MC44NDcyQzE3Ljg1MjkgNjAuODQ3MiAxOC4wNjEzIDYwLjkwMDYgMTguMjMzMiA2MS4wMDczQzE4LjQwNzYgNjEuMTE0MSAxOC41NDE4IDYxLjI1NiAxOC42MzU1IDYxLjQzMzFDMTguNzI5MyA2MS42MTAyIDE4Ljc3NjEgNjEuODA1NSAxOC43NzYxIDYyLjAxOVY2Mi4zMjM3QzE4Ljc3NjEgNjIuNTM3MyAxOC43MjkzIDYyLjczMjYgMTguNjM1NSA2Mi45MDk3QzE4LjU0NDQgNjMuMDg2OCAxOC40MTE1IDYzLjIyODcgMTguMjM3MSA2My4zMzU0QzE4LjA2NTIgNjMuNDQyMiAxNy44NTgyIDYzLjQ5NTYgMTcuNjE2IDYzLjQ5NTZDMTcuMzczOCA2My40OTU2IDE3LjE2NTQgNjMuNDQyMiAxNi45OTEgNjMuMzM1NEMxNi44MTY1IDYzLjIyODcgMTYuNjgxMSA2My4wODY4IDE2LjU4NDcgNjIuOTA5N0MxNi40OTEgNjIuNzMyNiAxNi40NDQxIDYyLjUzNzMgMTYuNDQ0MSA2Mi4zMjM3Wk0xNi45ODcxIDYyLjAxOVY2Mi4zMjM3QzE2Ljk4NzEgNjIuNDQzNSAxNy4wMDkyIDYyLjU1ODEgMTcuMDUzNSA2Mi42Njc1QzE3LjEwMDMgNjIuNzc0MyAxNy4xNzA3IDYyLjg2MTUgMTcuMjY0NCA2Mi45MjkyQzE3LjM1ODIgNjIuOTk0MyAxNy40NzUzIDYzLjAyNjkgMTcuNjE2IDYzLjAyNjlDMTcuNzU2NiA2My4wMjY5IDE3Ljg3MjUgNjIuOTk0MyAxNy45NjM2IDYyLjkyOTJDMTguMDU3NCA2Mi44NjE1IDE4LjEyNjQgNjIuNzc0MyAxOC4xNzA3IDYyLjY2NzVDMTguMjE0OSA2Mi41NjA3IDE4LjIzNzEgNjIuNDQ2MSAxOC4yMzcxIDYyLjMyMzdWNjIuMDE5QzE4LjIzNzEgNjEuODk2NiAxOC4yMTM2IDYxLjc4MjEgMTguMTY2OCA2MS42NzUzQzE4LjEyMjUgNjEuNTY4NSAxOC4wNTM1IDYxLjQ4MjYgMTcuOTU5NyA2MS40MTc1QzE3Ljg2ODYgNjEuMzQ5OCAxNy43NTE0IDYxLjMxNTkgMTcuNjA4MiA2MS4zMTU5QzE3LjQ3MDEgNjEuMzE1OSAxNy4zNTQzIDYxLjM0OTggMTcuMjYwNSA2MS40MTc1QzE3LjE2OTQgNjEuNDgyNiAxNy4xMDAzIDYxLjU2ODUgMTcuMDUzNSA2MS42NzUzQzE3LjAwOTIgNjEuNzgyMSAxNi45ODcxIDYxLjg5NjYgMTYuOTg3MSA2Mi4wMTlaTTE3Ljc4NzggNTguNTM0N0wxNS4wMTA1IDYyLjk4TDE0LjYwNDMgNjIuNzIyMkwxNy4zODE2IDU4LjI3NjlMMTcuNzg3OCA1OC41MzQ3WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNOC4zMTY0MSA4OS43MDYzVjkwLjNINC4yMDcwM1Y4OS44NzQzTDYuNzUzOTEgODUuOTMyOUg3LjM0Mzc1TDYuNzEwOTQgODcuMDczNUw1LjAyNzM0IDg5LjcwNjNIOC4zMTY0MVpNNy41MjM0NCA4NS45MzI5VjkxLjYyMDRINi44MDA3OFY4NS45MzI5SDcuNTIzNDRaTTEyLjY3NTIgODguMzIzNVY4OS4xOTA3QzEyLjY3NTIgODkuNjU2OCAxMi42MzM1IDkwLjA1IDEyLjU1MDIgOTAuMzcwNEMxMi40NjY4IDkwLjY5MDcgMTIuMzQ3IDkwLjk0ODUgMTIuMTkwOCA5MS4xNDM4QzEyLjAzNDUgOTEuMzM5MSAxMS44NDU3IDkxLjQ4MSAxMS42MjQ0IDkxLjU2OTZDMTEuNDA1NiA5MS42NTU1IDExLjE1ODIgOTEuNjk4NSAxMC44ODIyIDkxLjY5ODVDMTAuNjYzNSA5MS42OTg1IDEwLjQ2MTYgOTEuNjcxMSAxMC4yNzY3IDkxLjYxNjVDMTAuMDkxOCA5MS41NjE4IDkuOTI1MTcgOTEuNDc0NSA5Ljc3NjczIDkxLjM1NDdDOS42MzA5IDkxLjIzMjMgOS41MDU5IDkxLjA3MzUgOS40MDE3MyA5MC44NzgyQzkuMjk3NTcgOTAuNjgyOSA5LjIxODE0IDkwLjQ0NTkgOS4xNjM0NSA5MC4xNjcyQzkuMTA4NzcgODkuODg4NiA5LjA4MTQyIDg5LjU2MzEgOS4wODE0MiA4OS4xOTA3Vjg4LjMyMzVDOS4wODE0MiA4Ny44NTczIDkuMTIzMDkgODcuNDY2NyA5LjIwNjQyIDg3LjE1MTZDOS4yOTIzNiA4Ni44MzY1IDkuNDEzNDUgODYuNTgzOSA5LjU2OTcgODYuMzkzOEM5LjcyNTk1IDg2LjIwMTEgOS45MTM0NSA4Ni4wNjMxIDEwLjEzMjIgODUuOTc5N0MxMC4zNTM2IDg1Ljg5NjQgMTAuNjAxIDg1Ljg1NDcgMTAuODc0NCA4NS44NTQ3QzExLjA5NTcgODUuODU0NyAxMS4yOTg5IDg1Ljg4MjEgMTEuNDgzOCA4NS45MzY4QzExLjY3MTMgODUuOTg4OSAxMS44Mzc5IDg2LjA3MzUgMTEuOTgzOCA4Ni4xOTA3QzEyLjEyOTYgODYuMzA1MyAxMi4yNTMzIDg2LjQ1ODkgMTIuMzU0OSA4Ni42NTE2QzEyLjQ1OSA4Ni44NDE3IDEyLjUzODUgODcuMDc0OCAxMi41OTMxIDg3LjM1MDhDMTIuNjQ3OCA4Ny42MjY5IDEyLjY3NTIgODcuOTUxMSAxMi42NzUyIDg4LjMyMzVaTTExLjk0ODYgODkuMzA3OVY4OC4yMDI0QzExLjk0ODYgODcuOTQ3MiAxMS45MzMgODcuNzIzMiAxMS45MDE3IDg3LjUzMDVDMTEuODczMSA4Ny4zMzUyIDExLjgzMDEgODcuMTY4NSAxMS43NzI4IDg3LjAzMDVDMTEuNzE1NSA4Ni44OTI1IDExLjY0MjYgODYuNzgwNSAxMS41NTQxIDg2LjY5NDZDMTEuNDY4MSA4Ni42MDg2IDExLjM2NzkgODYuNTQ2MSAxMS4yNTMzIDg2LjUwNzFDMTEuMTQxMyA4Ni40NjU0IDExLjAxNSA4Ni40NDQ2IDEwLjg3NDQgODYuNDQ0NkMxMC43MDI1IDg2LjQ0NDYgMTAuNTUwMiA4Ni40NzcxIDEwLjQxNzQgODYuNTQyMkMxMC4yODQ1IDg2LjYwNDcgMTAuMTcyNiA4Ni43MDUgMTAuMDgxNCA4Ni44NDNDOS45OTI4OCA4Ni45ODEgOS45MjUxNyA4Ny4xNjIgOS44NzgzIDg3LjM4NkM5LjgzMTQyIDg3LjYwOTkgOS44MDc5OCA4Ny44ODIxIDkuODA3OTggODguMjAyNFY4OS4zMDc5QzkuODA3OTggODkuNTYzMSA5LjgyMjMxIDg5Ljc4ODMgOS44NTA5NSA4OS45ODM2QzkuODgyMiA5MC4xNzkgOS45Mjc3OCA5MC4zNDgyIDkuOTg3NjcgOTAuNDkxNUMxMC4wNDc2IDkwLjYzMjEgMTAuMTIwNSA5MC43NDggMTAuMjA2NCA5MC44MzkxQzEwLjI5MjQgOTAuOTMwMyAxMC4zOTEzIDkwLjk5OCAxMC41MDMzIDkxLjA0MjJDMTAuNjE3OSA5MS4wODM5IDEwLjc0NDIgOTEuMTA0NyAxMC44ODIyIDkxLjEwNDdDMTEuMDU5MyA5MS4xMDQ3IDExLjIxNDIgOTEuMDcwOSAxMS4zNDcgOTEuMDAzMkMxMS40Nzk5IDkwLjkzNTUgMTEuNTkwNSA5MC44MyAxMS42NzkxIDkwLjY4NjhDMTEuNzcwMiA5MC41NDA5IDExLjgzNzkgOTAuMzU0NyAxMS44ODIyIDkwLjEyODJDMTEuOTI2NSA4OS44OTkgMTEuOTQ4NiA4OS42MjU2IDExLjk0ODYgODkuMzA3OVpNMTMuNjc0NiA4Ny4zMjc0Vjg3LjAyNjZDMTMuNjc0NiA4Ni44MTA1IDEzLjcyMTQgODYuNjEzOSAxMy44MTUyIDg2LjQzNjhDMTMuOTA4OSA4Ni4yNTk3IDE0LjA0MzEgODYuMTE3OCAxNC4yMTc1IDg2LjAxMUMxNC4zOTIgODUuOTA0MiAxNC41OTkgODUuODUwOCAxNC44Mzg2IDg1Ljg1MDhDMTUuMDgzNCA4NS44NTA4IDE1LjI5MTggODUuOTA0MiAxNS40NjM2IDg2LjAxMUMxNS42MzgxIDg2LjExNzggMTUuNzcyMiA4Ni4yNTk3IDE1Ljg2NiA4Ni40MzY4QzE1Ljk1OTcgODYuNjEzOSAxNi4wMDY2IDg2LjgxMDUgMTYuMDA2NiA4Ny4wMjY2Vjg3LjMyNzRDMTYuMDA2NiA4Ny41MzgzIDE1Ljk1OTcgODcuNzMyMyAxNS44NjYgODcuOTA5NEMxNS43NzQ4IDg4LjA4NjUgMTUuNjQyIDg4LjIyODQgMTUuNDY3NSA4OC4zMzUyQzE1LjI5NTcgODguNDQyIDE1LjA4ODYgODguNDk1NCAxNC44NDY0IDg4LjQ5NTRDMTQuNjA0MyA4OC40OTU0IDE0LjM5NDYgODguNDQyIDE0LjIxNzUgODguMzM1MkMxNC4wNDMxIDg4LjIyODQgMTMuOTA4OSA4OC4wODY1IDEzLjgxNTIgODcuOTA5NEMxMy43MjE0IDg3LjczMjMgMTMuNjc0NiA4Ny41MzgzIDEzLjY3NDYgODcuMzI3NFpNMTQuMjE3NSA4Ny4wMjY2Vjg3LjMyNzRDMTQuMjE3NSA4Ny40NDcyIDE0LjIzOTcgODcuNTYwNSAxNC4yODM5IDg3LjY2NzJDMTQuMzMwOCA4Ny43NzQgMTQuNDAxMSA4Ny44NjEyIDE0LjQ5NDkgODcuOTI5QzE0LjU4ODYgODcuOTk0MSAxNC43MDU4IDg4LjAyNjYgMTQuODQ2NCA4OC4wMjY2QzE0Ljk4NzEgODguMDI2NiAxNS4xMDI5IDg3Ljk5NDEgMTUuMTk0MSA4Ny45MjlDMTUuMjg1MiA4Ny44NjEyIDE1LjM1MjkgODcuNzc0IDE1LjM5NzIgODcuNjY3MkMxNS40NDE1IDg3LjU2MDUgMTUuNDYzNiA4Ny40NDcyIDE1LjQ2MzYgODcuMzI3NFY4Ny4wMjY2QzE1LjQ2MzYgODYuOTA0MiAxNS40NDAyIDg2Ljc4OTYgMTUuMzkzMyA4Ni42ODI5QzE1LjM0OSA4Ni41NzM1IDE1LjI4IDg2LjQ4NjIgMTUuMTg2MyA4Ni40MjExQzE1LjA5NTEgODYuMzUzNCAxNC45NzkzIDg2LjMxOTYgMTQuODM4NiA4Ni4zMTk2QzE0LjcwMDYgODYuMzE5NiAxNC41ODQ3IDg2LjM1MzQgMTQuNDkxIDg2LjQyMTFDMTQuMzk5OCA4Ni40ODYyIDE0LjMzMDggODYuNTczNSAxNC4yODM5IDg2LjY4MjlDMTQuMjM5NyA4Ni43ODk2IDE0LjIxNzUgODYuOTA0MiAxNC4yMTc1IDg3LjAyNjZaTTE2LjQ0NDEgOTAuNTMwNVY5MC4yMjU4QzE2LjQ0NDEgOTAuMDEyMyAxNi40OTEgODkuODE3IDE2LjU4NDcgODkuNjM5OUMxNi42Nzg1IDg5LjQ2MjggMTYuODEyNiA4OS4zMjA5IDE2Ljk4NzEgODkuMjE0MUMxNy4xNjE1IDg5LjEwNzMgMTcuMzY4NiA4OS4wNTQgMTcuNjA4MiA4OS4wNTRDMTcuODUyOSA4OS4wNTQgMTguMDYxMyA4OS4xMDczIDE4LjIzMzIgODkuMjE0MUMxOC40MDc2IDg5LjMyMDkgMTguNTQxOCA4OS40NjI4IDE4LjYzNTUgODkuNjM5OUMxOC43MjkzIDg5LjgxNyAxOC43NzYxIDkwLjAxMjMgMTguNzc2MSA5MC4yMjU4VjkwLjUzMDVDMTguNzc2MSA5MC43NDQxIDE4LjcyOTMgOTAuOTM5NCAxOC42MzU1IDkxLjExNjVDMTguNTQ0NCA5MS4yOTM1IDE4LjQxMTUgOTEuNDM1NSAxOC4yMzcxIDkxLjU0MjJDMTguMDY1MiA5MS42NDkgMTcuODU4MiA5MS43MDI0IDE3LjYxNiA5MS43MDI0QzE3LjM3MzggOTEuNzAyNCAxNy4xNjU0IDkxLjY0OSAxNi45OTEgOTEuNTQyMkMxNi44MTY1IDkxLjQzNTUgMTYuNjgxMSA5MS4yOTM1IDE2LjU4NDcgOTEuMTE2NUMxNi40OTEgOTAuOTM5NCAxNi40NDQxIDkwLjc0NDEgMTYuNDQ0MSA5MC41MzA1Wk0xNi45ODcxIDkwLjIyNThWOTAuNTMwNUMxNi45ODcxIDkwLjY1MDMgMTcuMDA5MiA5MC43NjQ5IDE3LjA1MzUgOTAuODc0M0MxNy4xMDAzIDkwLjk4MSAxNy4xNzA3IDkxLjA2ODMgMTcuMjY0NCA5MS4xMzZDMTcuMzU4MiA5MS4yMDExIDE3LjQ3NTMgOTEuMjMzNiAxNy42MTYgOTEuMjMzNkMxNy43NTY2IDkxLjIzMzYgMTcuODcyNSA5MS4yMDExIDE3Ljk2MzYgOTEuMTM2QzE4LjA1NzQgOTEuMDY4MyAxOC4xMjY0IDkwLjk4MSAxOC4xNzA3IDkwLjg3NDNDMTguMjE0OSA5MC43Njc1IDE4LjIzNzEgOTAuNjUyOSAxOC4yMzcxIDkwLjUzMDVWOTAuMjI1OEMxOC4yMzcxIDkwLjEwMzQgMTguMjEzNiA4OS45ODg5IDE4LjE2NjggODkuODgyMUMxOC4xMjI1IDg5Ljc3NTMgMTguMDUzNSA4OS42ODk0IDE3Ljk1OTcgODkuNjI0M0MxNy44Njg2IDg5LjU1NjYgMTcuNzUxNCA4OS41MjI3IDE3LjYwODIgODkuNTIyN0MxNy40NzAxIDg5LjUyMjcgMTcuMzU0MyA4OS41NTY2IDE3LjI2MDUgODkuNjI0M0MxNy4xNjk0IDg5LjY4OTQgMTcuMTAwMyA4OS43NzUzIDE3LjA1MzUgODkuODgyMUMxNy4wMDkyIDg5Ljk4ODkgMTYuOTg3MSA5MC4xMDM0IDE2Ljk4NzEgOTAuMjI1OFpNMTcuNzg3OCA4Ni43NDE1TDE1LjAxMDUgOTEuMTg2OEwxNC42MDQzIDkwLjkyOUwxNy4zODE2IDg2LjQ4MzZMMTcuNzg3OCA4Ni43NDE1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNOC4xOTkyMiAxMTkuMjMzVjExOS44MjdINC40NzY1NlYxMTkuMzA4TDYuMzM5ODQgMTE3LjIzM0M2LjU2OTAxIDExNi45NzggNi43NDYwOSAxMTYuNzYyIDYuODcxMDkgMTE2LjU4NUM2Ljk5ODcgMTE2LjQwNSA3LjA4NzI0IDExNi4yNDUgNy4xMzY3MiAxMTYuMTA0QzcuMTg4OCAxMTUuOTYxIDcuMjE0ODQgMTE1LjgxNSA3LjIxNDg0IDExNS42NjdDNy4yMTQ4NCAxMTUuNDc5IDcuMTc1NzggMTE1LjMxIDcuMDk3NjYgMTE1LjE1OUM3LjAyMjE0IDExNS4wMDYgNi45MTAxNiAxMTQuODgzIDYuNzYxNzIgMTE0Ljc5MkM2LjYxMzI4IDExNC43MDEgNi40MzM1OSAxMTQuNjU1IDYuMjIyNjYgMTE0LjY1NUM1Ljk3MDA1IDExNC42NTUgNS43NTkxMSAxMTQuNzA1IDUuNTg5ODQgMTE0LjgwNEM1LjQyMzE4IDExNC45IDUuMjk4MTggMTE1LjAzNSA1LjIxNDg0IDExNS4yMUM1LjEzMTUxIDExNS4zODQgNS4wODk4NCAxMTUuNTg1IDUuMDg5ODQgMTE1LjgxMkg0LjM2NzE5QzQuMzY3MTkgMTE1LjQ5MSA0LjQzNzUgMTE1LjE5OCA0LjU3ODEyIDExNC45MzNDNC43MTg3NSAxMTQuNjY3IDQuOTI3MDggMTE0LjQ1NiA1LjIwMzEyIDExNC4zQzUuNDc5MTcgMTE0LjE0MSA1LjgxOTAxIDExNC4wNjIgNi4yMjI2NiAxMTQuMDYyQzYuNTgyMDMgMTE0LjA2MiA2Ljg4OTMyIDExNC4xMjUgNy4xNDQ1MyAxMTQuMjUzQzcuMzk5NzQgMTE0LjM3OCA3LjU5NTA1IDExNC41NTUgNy43MzA0NyAxMTQuNzg0QzcuODY4NDkgMTE1LjAxMSA3LjkzNzUgMTE1LjI3NiA3LjkzNzUgMTE1LjU4MUM3LjkzNzUgMTE1Ljc0OCA3LjkwODg1IDExNS45MTcgNy44NTE1NiAxMTYuMDg5QzcuNzk2ODggMTE2LjI1OCA3LjcyMDA1IDExNi40MjcgNy42MjEwOSAxMTYuNTk3QzcuNTI0NzQgMTE2Ljc2NiA3LjQxMTQ2IDExNi45MzMgNy4yODEyNSAxMTcuMDk3QzcuMTUzNjUgMTE3LjI2MSA3LjAxNjkzIDExNy40MjIgNi44NzEwOSAxMTcuNTgxTDUuMzQ3NjYgMTE5LjIzM0g4LjE5OTIyWk0xMi42NzUyIDExNi41M1YxMTcuMzk3QzEyLjY3NTIgMTE3Ljg2NCAxMi42MzM1IDExOC4yNTcgMTIuNTUwMiAxMTguNTc3QzEyLjQ2NjggMTE4Ljg5NyAxMi4zNDcgMTE5LjE1NSAxMi4xOTA4IDExOS4zNTFDMTIuMDM0NSAxMTkuNTQ2IDExLjg0NTcgMTE5LjY4OCAxMS42MjQ0IDExOS43NzZDMTEuNDA1NiAxMTkuODYyIDExLjE1ODIgMTE5LjkwNSAxMC44ODIyIDExOS45MDVDMTAuNjYzNSAxMTkuOTA1IDEwLjQ2MTYgMTE5Ljg3OCAxMC4yNzY3IDExOS44MjNDMTAuMDkxOCAxMTkuNzY5IDkuOTI1MTcgMTE5LjY4MSA5Ljc3NjczIDExOS41NjJDOS42MzA5IDExOS40MzkgOS41MDU5IDExOS4yOCA5LjQwMTczIDExOS4wODVDOS4yOTc1NyAxMTguODkgOS4yMTgxNCAxMTguNjUzIDkuMTYzNDUgMTE4LjM3NEM5LjEwODc3IDExOC4wOTUgOS4wODE0MiAxMTcuNzcgOS4wODE0MiAxMTcuMzk3VjExNi41M0M5LjA4MTQyIDExNi4wNjQgOS4xMjMwOSAxMTUuNjc0IDkuMjA2NDIgMTE1LjM1OEM5LjI5MjM2IDExNS4wNDMgOS40MTM0NSAxMTQuNzkxIDkuNTY5NyAxMTQuNjAxQzkuNzI1OTUgMTE0LjQwOCA5LjkxMzQ1IDExNC4yNyAxMC4xMzIyIDExNC4xODdDMTAuMzUzNiAxMTQuMTAzIDEwLjYwMSAxMTQuMDYyIDEwLjg3NDQgMTE0LjA2MkMxMS4wOTU3IDExNC4wNjIgMTEuMjk4OSAxMTQuMDg5IDExLjQ4MzggMTE0LjE0NEMxMS42NzEzIDExNC4xOTYgMTEuODM3OSAxMTQuMjggMTEuOTgzOCAxMTQuMzk3QzEyLjEyOTYgMTE0LjUxMiAxMi4yNTMzIDExNC42NjYgMTIuMzU0OSAxMTQuODU4QzEyLjQ1OSAxMTUuMDQ5IDEyLjUzODUgMTE1LjI4MiAxMi41OTMxIDExNS41NThDMTIuNjQ3OCAxMTUuODM0IDEyLjY3NTIgMTE2LjE1OCAxMi42NzUyIDExNi41M1pNMTEuOTQ4NiAxMTcuNTE1VjExNi40MDlDMTEuOTQ4NiAxMTYuMTU0IDExLjkzMyAxMTUuOTMgMTEuOTAxNyAxMTUuNzM3QzExLjg3MzEgMTE1LjU0MiAxMS44MzAxIDExNS4zNzUgMTEuNzcyOCAxMTUuMjM3QzExLjcxNTUgMTE1LjA5OSAxMS42NDI2IDExNC45ODcgMTEuNTU0MSAxMTQuOTAxQzExLjQ2ODEgMTE0LjgxNSAxMS4zNjc5IDExNC43NTMgMTEuMjUzMyAxMTQuNzE0QzExLjE0MTMgMTE0LjY3MiAxMS4wMTUgMTE0LjY1MSAxMC44NzQ0IDExNC42NTFDMTAuNzAyNSAxMTQuNjUxIDEwLjU1MDIgMTE0LjY4NCAxMC40MTc0IDExNC43NDlDMTAuMjg0NSAxMTQuODEyIDEwLjE3MjYgMTE0LjkxMiAxMC4wODE0IDExNS4wNUM5Ljk5Mjg4IDExNS4xODggOS45MjUxNyAxMTUuMzY5IDkuODc4MyAxMTUuNTkzQzkuODMxNDIgMTE1LjgxNyA5LjgwNzk4IDExNi4wODkgOS44MDc5OCAxMTYuNDA5VjExNy41MTVDOS44MDc5OCAxMTcuNzcgOS44MjIzMSAxMTcuOTk1IDkuODUwOTUgMTE4LjE5QzkuODgyMiAxMTguMzg2IDkuOTI3NzggMTE4LjU1NSA5Ljk4NzY3IDExOC42OThDMTAuMDQ3NiAxMTguODM5IDEwLjEyMDUgMTE4Ljk1NSAxMC4yMDY0IDExOS4wNDZDMTAuMjkyNCAxMTkuMTM3IDEwLjM5MTMgMTE5LjIwNSAxMC41MDMzIDExOS4yNDlDMTAuNjE3OSAxMTkuMjkxIDEwLjc0NDIgMTE5LjMxMiAxMC44ODIyIDExOS4zMTJDMTEuMDU5MyAxMTkuMzEyIDExLjIxNDIgMTE5LjI3OCAxMS4zNDcgMTE5LjIxQzExLjQ3OTkgMTE5LjE0MiAxMS41OTA1IDExOS4wMzcgMTEuNjc5MSAxMTguODk0QzExLjc3MDIgMTE4Ljc0OCAxMS44Mzc5IDExOC41NjIgMTEuODgyMiAxMTguMzM1QzExLjkyNjUgMTE4LjEwNiAxMS45NDg2IDExNy44MzIgMTEuOTQ4NiAxMTcuNTE1Wk0xMy42NzQ2IDExNS41MzRWMTE1LjIzM0MxMy42NzQ2IDExNS4wMTcgMTMuNzIxNCAxMTQuODIxIDEzLjgxNTIgMTE0LjY0NEMxMy45MDg5IDExNC40NjYgMTQuMDQzMSAxMTQuMzI1IDE0LjIxNzUgMTE0LjIxOEMxNC4zOTIgMTE0LjExMSAxNC41OTkgMTE0LjA1OCAxNC44Mzg2IDExNC4wNThDMTUuMDgzNCAxMTQuMDU4IDE1LjI5MTggMTE0LjExMSAxNS40NjM2IDExNC4yMThDMTUuNjM4MSAxMTQuMzI1IDE1Ljc3MjIgMTE0LjQ2NiAxNS44NjYgMTE0LjY0NEMxNS45NTk3IDExNC44MjEgMTYuMDA2NiAxMTUuMDE3IDE2LjAwNjYgMTE1LjIzM1YxMTUuNTM0QzE2LjAwNjYgMTE1Ljc0NSAxNS45NTk3IDExNS45MzkgMTUuODY2IDExNi4xMTZDMTUuNzc0OCAxMTYuMjkzIDE1LjY0MiAxMTYuNDM1IDE1LjQ2NzUgMTE2LjU0MkMxNS4yOTU3IDExNi42NDkgMTUuMDg4NiAxMTYuNzAyIDE0Ljg0NjQgMTE2LjcwMkMxNC42MDQzIDExNi43MDIgMTQuMzk0NiAxMTYuNjQ5IDE0LjIxNzUgMTE2LjU0MkMxNC4wNDMxIDExNi40MzUgMTMuOTA4OSAxMTYuMjkzIDEzLjgxNTIgMTE2LjExNkMxMy43MjE0IDExNS45MzkgMTMuNjc0NiAxMTUuNzQ1IDEzLjY3NDYgMTE1LjUzNFpNMTQuMjE3NSAxMTUuMjMzVjExNS41MzRDMTQuMjE3NSAxMTUuNjU0IDE0LjIzOTcgMTE1Ljc2NyAxNC4yODM5IDExNS44NzRDMTQuMzMwOCAxMTUuOTgxIDE0LjQwMTEgMTE2LjA2OCAxNC40OTQ5IDExNi4xMzZDMTQuNTg4NiAxMTYuMjAxIDE0LjcwNTggMTE2LjIzMyAxNC44NDY0IDExNi4yMzNDMTQuOTg3MSAxMTYuMjMzIDE1LjEwMjkgMTE2LjIwMSAxNS4xOTQxIDExNi4xMzZDMTUuMjg1MiAxMTYuMDY4IDE1LjM1MjkgMTE1Ljk4MSAxNS4zOTcyIDExNS44NzRDMTUuNDQxNSAxMTUuNzY3IDE1LjQ2MzYgMTE1LjY1NCAxNS40NjM2IDExNS41MzRWMTE1LjIzM0MxNS40NjM2IDExNS4xMTEgMTUuNDQwMiAxMTQuOTk2IDE1LjM5MzMgMTE0Ljg5QzE1LjM0OSAxMTQuNzggMTUuMjggMTE0LjY5MyAxNS4xODYzIDExNC42MjhDMTUuMDk1MSAxMTQuNTYgMTQuOTc5MyAxMTQuNTI2IDE0LjgzODYgMTE0LjUyNkMxNC43MDA2IDExNC41MjYgMTQuNTg0NyAxMTQuNTYgMTQuNDkxIDExNC42MjhDMTQuMzk5OCAxMTQuNjkzIDE0LjMzMDggMTE0Ljc4IDE0LjI4MzkgMTE0Ljg5QzE0LjIzOTcgMTE0Ljk5NiAxNC4yMTc1IDExNS4xMTEgMTQuMjE3NSAxMTUuMjMzWk0xNi40NDQxIDExOC43MzdWMTE4LjQzM0MxNi40NDQxIDExOC4yMTkgMTYuNDkxIDExOC4wMjQgMTYuNTg0NyAxMTcuODQ3QzE2LjY3ODUgMTE3LjY3IDE2LjgxMjYgMTE3LjUyOCAxNi45ODcxIDExNy40MjFDMTcuMTYxNSAxMTcuMzE0IDE3LjM2ODYgMTE3LjI2MSAxNy42MDgyIDExNy4yNjFDMTcuODUyOSAxMTcuMjYxIDE4LjA2MTMgMTE3LjMxNCAxOC4yMzMyIDExNy40MjFDMTguNDA3NiAxMTcuNTI4IDE4LjU0MTggMTE3LjY3IDE4LjYzNTUgMTE3Ljg0N0MxOC43MjkzIDExOC4wMjQgMTguNzc2MSAxMTguMjE5IDE4Ljc3NjEgMTE4LjQzM1YxMTguNzM3QzE4Ljc3NjEgMTE4Ljk1MSAxOC43MjkzIDExOS4xNDYgMTguNjM1NSAxMTkuMzIzQzE4LjU0NDQgMTE5LjUgMTguNDExNSAxMTkuNjQyIDE4LjIzNzEgMTE5Ljc0OUMxOC4wNjUyIDExOS44NTYgMTcuODU4MiAxMTkuOTA5IDE3LjYxNiAxMTkuOTA5QzE3LjM3MzggMTE5LjkwOSAxNy4xNjU0IDExOS44NTYgMTYuOTkxIDExOS43NDlDMTYuODE2NSAxMTkuNjQyIDE2LjY4MTEgMTE5LjUgMTYuNTg0NyAxMTkuMzIzQzE2LjQ5MSAxMTkuMTQ2IDE2LjQ0NDEgMTE4Ljk1MSAxNi40NDQxIDExOC43MzdaTTE2Ljk4NzEgMTE4LjQzM1YxMTguNzM3QzE2Ljk4NzEgMTE4Ljg1NyAxNy4wMDkyIDExOC45NzIgMTcuMDUzNSAxMTkuMDgxQzE3LjEwMDMgMTE5LjE4OCAxNy4xNzA3IDExOS4yNzUgMTcuMjY0NCAxMTkuMzQzQzE3LjM1ODIgMTE5LjQwOCAxNy40NzUzIDExOS40NCAxNy42MTYgMTE5LjQ0QzE3Ljc1NjYgMTE5LjQ0IDE3Ljg3MjUgMTE5LjQwOCAxNy45NjM2IDExOS4zNDNDMTguMDU3NCAxMTkuMjc1IDE4LjEyNjQgMTE5LjE4OCAxOC4xNzA3IDExOS4wODFDMTguMjE0OSAxMTguOTc0IDE4LjIzNzEgMTE4Ljg2IDE4LjIzNzEgMTE4LjczN1YxMTguNDMzQzE4LjIzNzEgMTE4LjMxIDE4LjIxMzYgMTE4LjE5NiAxOC4xNjY4IDExOC4wODlDMTguMTIyNSAxMTcuOTgyIDE4LjA1MzUgMTE3Ljg5NiAxNy45NTk3IDExNy44MzFDMTcuODY4NiAxMTcuNzYzIDE3Ljc1MTQgMTE3LjcyOSAxNy42MDgyIDExNy43MjlDMTcuNDcwMSAxMTcuNzI5IDE3LjM1NDMgMTE3Ljc2MyAxNy4yNjA1IDExNy44MzFDMTcuMTY5NCAxMTcuODk2IDE3LjEwMDMgMTE3Ljk4MiAxNy4wNTM1IDExOC4wODlDMTcuMDA5MiAxMTguMTk2IDE2Ljk4NzEgMTE4LjMxIDE2Ljk4NzEgMTE4LjQzM1pNMTcuNzg3OCAxMTQuOTQ4TDE1LjAxMDUgMTE5LjM5NEwxNC42MDQzIDExOS4xMzZMMTcuMzgxNiAxMTQuNjlMMTcuNzg3OCAxMTQuOTQ4WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNMTMuMDQzIDE0NC43MzdWMTQ1LjYwNEMxMy4wNDMgMTQ2LjA3IDEzLjAwMTMgMTQ2LjQ2NCAxMi45MTggMTQ2Ljc4NEMxMi44MzQ2IDE0Ny4xMDQgMTIuNzE0OCAxNDcuMzYyIDEyLjU1ODYgMTQ3LjU1N0MxMi40MDIzIDE0Ny43NTMgMTIuMjEzNSAxNDcuODk1IDExLjk5MjIgMTQ3Ljk4M0MxMS43NzM0IDE0OC4wNjkgMTEuNTI2IDE0OC4xMTIgMTEuMjUgMTQ4LjExMkMxMS4wMzEyIDE0OC4xMTIgMTAuODI5NCAxNDguMDg1IDEwLjY0NDUgMTQ4LjAzQzEwLjQ1OTYgMTQ3Ljk3NSAxMC4yOTMgMTQ3Ljg4OCAxMC4xNDQ1IDE0Ny43NjhDOS45OTg3IDE0Ny42NDYgOS44NzM3IDE0Ny40ODcgOS43Njk1MyAxNDcuMjkyQzkuNjY1MzYgMTQ3LjA5NiA5LjU4NTk0IDE0Ni44NTkgOS41MzEyNSAxNDYuNTgxQzkuNDc2NTYgMTQ2LjMwMiA5LjQ0OTIyIDE0NS45NzcgOS40NDkyMiAxNDUuNjA0VjE0NC43MzdDOS40NDkyMiAxNDQuMjcxIDkuNDkwODkgMTQzLjg4IDkuNTc0MjIgMTQzLjU2NUM5LjY2MDE2IDE0My4yNSA5Ljc4MTI1IDE0Mi45OTcgOS45Mzc1IDE0Mi44MDdDMTAuMDkzOCAxNDIuNjE1IDEwLjI4MTIgMTQyLjQ3NyAxMC41IDE0Mi4zOTNDMTAuNzIxNCAxNDIuMzEgMTAuOTY4OCAxNDIuMjY4IDExLjI0MjIgMTQyLjI2OEMxMS40NjM1IDE0Mi4yNjggMTEuNjY2NyAxNDIuMjk2IDExLjg1MTYgMTQyLjM1QzEyLjAzOTEgMTQyLjQwMiAxMi4yMDU3IDE0Mi40ODcgMTIuMzUxNiAxNDIuNjA0QzEyLjQ5NzQgMTQyLjcxOSAxMi42MjExIDE0Mi44NzIgMTIuNzIyNyAxNDMuMDY1QzEyLjgyNjggMTQzLjI1NSAxMi45MDYyIDE0My40ODggMTIuOTYwOSAxNDMuNzY0QzEzLjAxNTYgMTQ0LjA0IDEzLjA0MyAxNDQuMzY1IDEzLjA0MyAxNDQuNzM3Wk0xMi4zMTY0IDE0NS43MjFWMTQ0LjYxNkMxMi4zMTY0IDE0NC4zNjEgMTIuMzAwOCAxNDQuMTM3IDEyLjI2OTUgMTQzLjk0NEMxMi4yNDA5IDE0My43NDkgMTIuMTk3OSAxNDMuNTgyIDEyLjE0MDYgMTQzLjQ0NEMxMi4wODMzIDE0My4zMDYgMTIuMDEwNCAxNDMuMTk0IDExLjkyMTkgMTQzLjEwOEMxMS44MzU5IDE0My4wMjIgMTEuNzM1NyAxNDIuOTYgMTEuNjIxMSAxNDIuOTIxQzExLjUwOTEgMTQyLjg3OSAxMS4zODI4IDE0Mi44NTggMTEuMjQyMiAxNDIuODU4QzExLjA3MDMgMTQyLjg1OCAxMC45MTggMTQyLjg5MSAxMC43ODUyIDE0Mi45NTZDMTAuNjUyMyAxNDMuMDE4IDEwLjU0MDQgMTQzLjExOSAxMC40NDkyIDE0My4yNTdDMTAuMzYwNyAxNDMuMzk1IDEwLjI5MyAxNDMuNTc2IDEwLjI0NjEgMTQzLjhDMTAuMTk5MiAxNDQuMDI0IDEwLjE3NTggMTQ0LjI5NiAxMC4xNzU4IDE0NC42MTZWMTQ1LjcyMUMxMC4xNzU4IDE0NS45NzcgMTAuMTkwMSAxNDYuMjAyIDEwLjIxODggMTQ2LjM5N0MxMC4yNSAxNDYuNTkzIDEwLjI5NTYgMTQ2Ljc2MiAxMC4zNTU1IDE0Ni45MDVDMTAuNDE1NCAxNDcuMDQ2IDEwLjQ4ODMgMTQ3LjE2MiAxMC41NzQyIDE0Ny4yNTNDMTAuNjYwMiAxNDcuMzQ0IDEwLjc1OTEgMTQ3LjQxMiAxMC44NzExIDE0Ny40NTZDMTAuOTg1NyAxNDcuNDk3IDExLjExMiAxNDcuNTE4IDExLjI1IDE0Ny41MThDMTEuNDI3MSAxNDcuNTE4IDExLjU4MiAxNDcuNDg0IDExLjcxNDggMTQ3LjQxN0MxMS44NDc3IDE0Ny4zNDkgMTEuOTU4MyAxNDcuMjQ0IDEyLjA0NjkgMTQ3LjFDMTIuMTM4IDE0Ni45NTUgMTIuMjA1NyAxNDYuNzY4IDEyLjI1IDE0Ni41NDJDMTIuMjk0MyAxNDYuMzEzIDEyLjMxNjQgMTQ2LjAzOSAxMi4zMTY0IDE0NS43MjFaTTE0LjA0MjQgMTQzLjc0MVYxNDMuNDRDMTQuMDQyNCAxNDMuMjI0IDE0LjA4OTIgMTQzLjAyNyAxNC4xODMgMTQyLjg1QzE0LjI3NjcgMTQyLjY3MyAxNC40MTA4IDE0Mi41MzEgMTQuNTg1MyAxNDIuNDI1QzE0Ljc1OTggMTQyLjMxOCAxNC45NjY4IDE0Mi4yNjQgMTUuMjA2NCAxNDIuMjY0QzE1LjQ1MTIgMTQyLjI2NCAxNS42NTk1IDE0Mi4zMTggMTUuODMxNCAxNDIuNDI1QzE2LjAwNTkgMTQyLjUzMSAxNi4xNCAxNDIuNjczIDE2LjIzMzggMTQyLjg1QzE2LjMyNzUgMTQzLjAyNyAxNi4zNzQ0IDE0My4yMjQgMTYuMzc0NCAxNDMuNDRWMTQzLjc0MUMxNi4zNzQ0IDE0My45NTIgMTYuMzI3NSAxNDQuMTQ2IDE2LjIzMzggMTQ0LjMyM0MxNi4xNDI2IDE0NC41IDE2LjAwOTggMTQ0LjY0MiAxNS44MzUzIDE0NC43NDlDMTUuNjYzNSAxNDQuODU2IDE1LjQ1NjQgMTQ0LjkwOSAxNS4yMTQyIDE0NC45MDlDMTQuOTcyIDE0NC45MDkgMTQuNzYyNCAxNDQuODU2IDE0LjU4NTMgMTQ0Ljc0OUMxNC40MTA4IDE0NC42NDIgMTQuMjc2NyAxNDQuNSAxNC4xODMgMTQ0LjMyM0MxNC4wODkyIDE0NC4xNDYgMTQuMDQyNCAxNDMuOTUyIDE0LjA0MjQgMTQzLjc0MVpNMTQuNTg1MyAxNDMuNDRWMTQzLjc0MUMxNC41ODUzIDE0My44NjEgMTQuNjA3NSAxNDMuOTc0IDE0LjY1MTcgMTQ0LjA4MUMxNC42OTg2IDE0NC4xODggMTQuNzY4OSAxNDQuMjc1IDE0Ljg2MjcgMTQ0LjM0M0MxNC45NTY0IDE0NC40MDggMTUuMDczNiAxNDQuNDQgMTUuMjE0MiAxNDQuNDRDMTUuMzU0OSAxNDQuNDQgMTUuNDcwNyAxNDQuNDA4IDE1LjU2MTkgMTQ0LjM0M0MxNS42NTMgMTQ0LjI3NSAxNS43MjA3IDE0NC4xODggMTUuNzY1IDE0NC4wODFDMTUuODA5MyAxNDMuOTc0IDE1LjgzMTQgMTQzLjg2MSAxNS44MzE0IDE0My43NDFWMTQzLjQ0QzE1LjgzMTQgMTQzLjMxOCAxNS44MDggMTQzLjIwMyAxNS43NjExIDE0My4wOTZDMTUuNzE2OCAxNDIuOTg3IDE1LjY0NzggMTQyLjkgMTUuNTU0MSAxNDIuODM1QzE1LjQ2MjkgMTQyLjc2NyAxNS4zNDcgMTQyLjczMyAxNS4yMDY0IDE0Mi43MzNDMTUuMDY4NCAxNDIuNzMzIDE0Ljk1MjUgMTQyLjc2NyAxNC44NTg4IDE0Mi44MzVDMTQuNzY3NiAxNDIuOSAxNC42OTg2IDE0Mi45ODcgMTQuNjUxNyAxNDMuMDk2QzE0LjYwNzUgMTQzLjIwMyAxNC41ODUzIDE0My4zMTggMTQuNTg1MyAxNDMuNDRaTTE2LjgxMTkgMTQ2Ljk0NFYxNDYuNjM5QzE2LjgxMTkgMTQ2LjQyNiAxNi44NTg4IDE0Ni4yMzEgMTYuOTUyNSAxNDYuMDUzQzE3LjA0NjMgMTQ1Ljg3NiAxNy4xODA0IDE0NS43MzQgMTcuMzU0OSAxNDUuNjI4QzE3LjUyOTMgMTQ1LjUyMSAxNy43MzY0IDE0NS40NjggMTcuOTc2IDE0NS40NjhDMTguMjIwNyAxNDUuNDY4IDE4LjQyOTEgMTQ1LjUyMSAxOC42MDEgMTQ1LjYyOEMxOC43NzU0IDE0NS43MzQgMTguOTA5NSAxNDUuODc2IDE5LjAwMzMgMTQ2LjA1M0MxOS4wOTcgMTQ2LjIzMSAxOS4xNDM5IDE0Ni40MjYgMTkuMTQzOSAxNDYuNjM5VjE0Ni45NDRDMTkuMTQzOSAxNDcuMTU4IDE5LjA5NyAxNDcuMzUzIDE5LjAwMzMgMTQ3LjUzQzE4LjkxMjIgMTQ3LjcwNyAxOC43NzkzIDE0Ny44NDkgMTguNjA0OSAxNDcuOTU2QzE4LjQzMyAxNDguMDYzIDE4LjIyNiAxNDguMTE2IDE3Ljk4MzggMTQ4LjExNkMxNy43NDE2IDE0OC4xMTYgMTcuNTMzMiAxNDguMDYzIDE3LjM1ODggMTQ3Ljk1NkMxNy4xODQzIDE0Ny44NDkgMTcuMDQ4OSAxNDcuNzA3IDE2Ljk1MjUgMTQ3LjUzQzE2Ljg1ODggMTQ3LjM1MyAxNi44MTE5IDE0Ny4xNTggMTYuODExOSAxNDYuOTQ0Wk0xNy4zNTQ5IDE0Ni42MzlWMTQ2Ljk0NEMxNy4zNTQ5IDE0Ny4wNjQgMTcuMzc3IDE0Ny4xNzggMTcuNDIxMyAxNDcuMjg4QzE3LjQ2ODEgMTQ3LjM5NSAxNy41Mzg1IDE0Ny40ODIgMTcuNjMyMiAxNDcuNTVDMTcuNzI2IDE0Ny42MTUgMTcuODQzMSAxNDcuNjQ3IDE3Ljk4MzggMTQ3LjY0N0MxOC4xMjQ0IDE0Ny42NDcgMTguMjQwMyAxNDcuNjE1IDE4LjMzMTQgMTQ3LjU1QzE4LjQyNTIgMTQ3LjQ4MiAxOC40OTQyIDE0Ny4zOTUgMTguNTM4NSAxNDcuMjg4QzE4LjU4MjcgMTQ3LjE4MSAxOC42MDQ5IDE0Ny4wNjYgMTguNjA0OSAxNDYuOTQ0VjE0Ni42MzlDMTguNjA0OSAxNDYuNTE3IDE4LjU4MTQgMTQ2LjQwMiAxOC41MzQ1IDE0Ni4yOTZDMTguNDkwMyAxNDYuMTg5IDE4LjQyMTMgMTQ2LjEwMyAxOC4zMjc1IDE0Ni4wMzhDMTguMjM2NCAxNDUuOTcgMTguMTE5MiAxNDUuOTM2IDE3Ljk3NiAxNDUuOTM2QzE3LjgzNzkgMTQ1LjkzNiAxNy43MjIgMTQ1Ljk3IDE3LjYyODMgMTQ2LjAzOEMxNy41MzcyIDE0Ni4xMDMgMTcuNDY4MSAxNDYuMTg5IDE3LjQyMTMgMTQ2LjI5NkMxNy4zNzcgMTQ2LjQwMiAxNy4zNTQ5IDE0Ni41MTcgMTcuMzU0OSAxNDYuNjM5Wk0xOC4xNTU2IDE0My4xNTVMMTUuMzc4MyAxNDcuNkwxNC45NzIgMTQ3LjM0M0wxNy43NDk0IDE0Mi44OTdMMTguMTU1NiAxNDMuMTU1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNMjUgNC4xNjExM0wyMDAgNC4xNjExNiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPHBhdGggZD0iTTI1IDMzLjE2MTFMMjAwIDMzLjE2MTIiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjEyIi8+CjxwYXRoIGQ9Ik0yNSA2MS4xNjExTDIwMCA2MS4xNjEyIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMjUgODkuMTYxMUwyMDAgODkuMTYxMiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPHBhdGggZD0iTTI1IDExOC4xNjFMMjAwIDExOC4xNjEiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjEyIi8+CjxsaW5lIHgxPSIyMy4yIiB5MT0iMTQ1Ljk2MSIgeDI9IjIwMi44IiB5Mj0iMTQ1Ljk2MSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNyIgc3Ryb2tlLXdpZHRoPSIwLjQiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPGxpbmUgeDE9IjQwLjQ1IiB5MT0iMTQ4LjA3MiIgeDI9IjQwLjQ1IiB5Mj0iMTQ3LjI1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNMzIuNTA5MSAxNTIuMDI1VjE1Mi44OTNDMzIuNTA5MSAxNTMuMzU5IDMyLjQ2NzQgMTUzLjc1MiAzMi4zODQxIDE1NC4wNzJDMzIuMzAwNyAxNTQuMzkzIDMyLjE4MDkgMTU0LjY1IDMyLjAyNDcgMTU0Ljg0NkMzMS44Njg0IDE1NS4wNDEgMzEuNjc5NiAxNTUuMTgzIDMxLjQ1ODMgMTU1LjI3MUMzMS4yMzk1IDE1NS4zNTcgMzAuOTkyMSAxNTUuNCAzMC43MTYxIDE1NS40QzMwLjQ5NzMgMTU1LjQgMzAuMjk1NSAxNTUuMzczIDMwLjExMDYgMTU1LjMxOEMyOS45MjU3IDE1NS4yNjQgMjkuNzU5MSAxNTUuMTc2IDI5LjYxMDYgMTU1LjA1N0MyOS40NjQ4IDE1NC45MzQgMjkuMzM5OCAxNTQuNzc1IDI5LjIzNTYgMTU0LjU4QzI5LjEzMTUgMTU0LjM4NSAyOS4wNTIgMTU0LjE0OCAyOC45OTczIDE1My44NjlDMjguOTQyNyAxNTMuNTkgMjguOTE1MyAxNTMuMjY1IDI4LjkxNTMgMTUyLjg5M1YxNTIuMDI1QzI4LjkxNTMgMTUxLjU1OSAyOC45NTcgMTUxLjE2OSAyOS4wNDAzIDE1MC44NTRDMjkuMTI2MiAxNTAuNTM4IDI5LjI0NzMgMTUwLjI4NiAyOS40MDM2IDE1MC4wOTZDMjkuNTU5OCAxNDkuOTAzIDI5Ljc0NzMgMTQ5Ljc2NSAyOS45NjYxIDE0OS42ODJDMzAuMTg3NCAxNDkuNTk4IDMwLjQzNDggMTQ5LjU1NyAzMC43MDgzIDE0OS41NTdDMzAuOTI5NiAxNDkuNTU3IDMxLjEzMjggMTQ5LjU4NCAzMS4zMTc3IDE0OS42MzlDMzEuNTA1MiAxNDkuNjkxIDMxLjY3MTggMTQ5Ljc3NSAzMS44MTc3IDE0OS44OTNDMzEuOTYzNSAxNTAuMDA3IDMyLjA4NzIgMTUwLjE2MSAzMi4xODg3IDE1MC4zNTRDMzIuMjkyOSAxNTAuNTQ0IDMyLjM3MjMgMTUwLjc3NyAzMi40MjcgMTUxLjA1M0MzMi40ODE3IDE1MS4zMjkgMzIuNTA5MSAxNTEuNjUzIDMyLjUwOTEgMTUyLjAyNVpNMzEuNzgyNSAxNTMuMDFWMTUxLjkwNEMzMS43ODI1IDE1MS42NDkgMzEuNzY2OSAxNTEuNDI1IDMxLjczNTYgMTUxLjIzMkMzMS43MDcgMTUxLjAzNyAzMS42NjQgMTUwLjg3IDMxLjYwNjcgMTUwLjczMkMzMS41NDk0IDE1MC41OTQgMzEuNDc2NSAxNTAuNDgyIDMxLjM4OCAxNTAuMzk2QzMxLjMwMiAxNTAuMzExIDMxLjIwMTggMTUwLjI0OCAzMS4wODcyIDE1MC4yMDlDMzAuOTc1MiAxNTAuMTY3IDMwLjg0ODkgMTUwLjE0NiAzMC43MDgzIDE1MC4xNDZDMzAuNTM2NCAxNTAuMTQ2IDMwLjM4NDEgMTUwLjE3OSAzMC4yNTEyIDE1MC4yNDRDMzAuMTE4NCAxNTAuMzA3IDMwLjAwNjUgMTUwLjQwNyAyOS45MTUzIDE1MC41NDVDMjkuODI2OCAxNTAuNjgzIDI5Ljc1OTEgMTUwLjg2NCAyOS43MTIyIDE1MS4wODhDMjkuNjY1MyAxNTEuMzEyIDI5LjY0MTkgMTUxLjU4NCAyOS42NDE5IDE1MS45MDRWMTUzLjAxQzI5LjY0MTkgMTUzLjI2NSAyOS42NTYyIDE1My40OSAyOS42ODQ4IDE1My42ODZDMjkuNzE2MSAxNTMuODgxIDI5Ljc2MTcgMTU0LjA1IDI5LjgyMTYgMTU0LjE5M0MyOS44ODE1IDE1NC4zMzQgMjkuOTU0NCAxNTQuNDUgMzAuMDQwMyAxNTQuNTQxQzMwLjEyNjIgMTU0LjYzMiAzMC4yMjUyIDE1NC43IDMwLjMzNzIgMTU0Ljc0NEMzMC40NTE4IDE1NC43ODYgMzAuNTc4MSAxNTQuODA3IDMwLjcxNjEgMTU0LjgwN0MzMC44OTMyIDE1NC44MDcgMzEuMDQ4MSAxNTQuNzczIDMxLjE4MDkgMTU0LjcwNUMzMS4zMTM3IDE1NC42MzcgMzEuNDI0NCAxNTQuNTMyIDMxLjUxMyAxNTQuMzg5QzMxLjYwNDEgMTU0LjI0MyAzMS42NzE4IDE1NC4wNTcgMzEuNzE2MSAxNTMuODNDMzEuNzYwNCAxNTMuNjAxIDMxLjc4MjUgMTUzLjMyNyAzMS43ODI1IDE1My4wMVpNMzUuODk2NCAxNDkuNjA0VjE1NS4zMjJIMzUuMTczN1YxNTAuNTA2TDMzLjcxNjcgMTUxLjAzN1YxNTAuMzg1TDM1Ljc4MzEgMTQ5LjYwNEgzNS44OTY0Wk00MS4xMTI0IDE0OS42MzVWMTU1LjMyMkg0MC4zNTg1VjE0OS42MzVINDEuMTEyNFpNNDMuNDk1MiAxNTIuMTkzVjE1Mi44MTFINDAuOTQ4M1YxNTIuMTkzSDQzLjQ5NTJaTTQzLjg4MTkgMTQ5LjYzNVYxNTAuMjUySDQwLjk0ODNWMTQ5LjYzNUg0My44ODE5Wk00Ni40MjE2IDE1NS40QzQ2LjEyNzMgMTU1LjQgNDUuODYwNCAxNTUuMzUxIDQ1LjYyMDggMTU1LjI1MkM0NS4zODM4IDE1NS4xNSA0NS4xNzk0IDE1NS4wMDggNDUuMDA3NSAxNTQuODI2QzQ0LjgzODMgMTU0LjY0NCA0NC43MDgxIDE1NC40MjggNDQuNjE2OSAxNTQuMTc4QzQ0LjUyNTggMTUzLjkyOCA0NC40ODAyIDE1My42NTQgNDQuNDgwMiAxNTMuMzU3VjE1My4xOTNDNDQuNDgwMiAxNTIuODUgNDQuNTMxIDE1Mi41NDQgNDQuNjMyNSAxNTIuMjc1QzQ0LjczNDEgMTUyLjAwNSA0NC44NzIxIDE1MS43NzUgNDUuMDQ2NiAxNTEuNTg4QzQ1LjIyMTEgMTUxLjQgNDUuNDE5IDE1MS4yNTggNDUuNjQwMyAxNTEuMTYyQzQ1Ljg2MTcgMTUxLjA2NiA0Ni4wOTA5IDE1MS4wMTggNDYuMzI3OCAxNTEuMDE4QzQ2LjYyOTkgMTUxLjAxOCA0Ni44OTAzIDE1MS4wNyA0Ny4xMDkxIDE1MS4xNzRDNDcuMzMwNSAxNTEuMjc4IDQ3LjUxMTQgMTUxLjQyNCA0Ny42NTIxIDE1MS42MTFDNDcuNzkyNyAxNTEuNzk2IDQ3Ljg5NjkgMTUyLjAxNSA0Ny45NjQ2IDE1Mi4yNjhDNDguMDMyMyAxNTIuNTE4IDQ4LjA2NjEgMTUyLjc5MSA0OC4wNjYxIDE1My4wODhWMTUzLjQxMkg0NC45MDk5VjE1Mi44MjJINDcuMzQzNVYxNTIuNzY4QzQ3LjMzMzEgMTUyLjU4IDQ3LjI5NCAxNTIuMzk4IDQ3LjIyNjMgMTUyLjIyMUM0Ny4xNjEyIDE1Mi4wNDQgNDcuMDU3IDE1MS44OTggNDYuOTEzOCAxNTEuNzgzQzQ2Ljc3MDYgMTUxLjY2OSA0Ni41NzUyIDE1MS42MTEgNDYuMzI3OCAxNTEuNjExQzQ2LjE2MzggMTUxLjYxMSA0Ni4wMTI3IDE1MS42NDYgNDUuODc0NyAxNTEuNzE3QzQ1LjczNjcgMTUxLjc4NSA0NS42MTgyIDE1MS44ODYgNDUuNTE5MyAxNTIuMDIxQzQ1LjQyMDMgMTUyLjE1NyA0NS4zNDM1IDE1Mi4zMjIgNDUuMjg4OCAxNTIuNTE4QzQ1LjIzNDEgMTUyLjcxMyA0NS4yMDY4IDE1Mi45MzggNDUuMjA2OCAxNTMuMTkzVjE1My4zNTdDNDUuMjA2OCAxNTMuNTU4IDQ1LjIzNDEgMTUzLjc0NyA0NS4yODg4IDE1My45MjRDNDUuMzQ2MSAxNTQuMDk4IDQ1LjQyODEgMTU0LjI1MiA0NS41MzQ5IDE1NC4zODVDNDUuNjQ0MyAxNTQuNTE4IDQ1Ljc3NTggMTU0LjYyMiA0NS45Mjk0IDE1NC42OTdDNDYuMDg1NyAxNTQuNzczIDQ2LjI2MjcgMTU0LjgxMSA0Ni40NjA3IDE1NC44MTFDNDYuNzE1OSAxNTQuODExIDQ2LjkzMiAxNTQuNzU4IDQ3LjEwOTEgMTU0LjY1NEM0Ny4yODYyIDE1NC41NSA0Ny40NDExIDE1NC40MTEgNDcuNTczOSAxNTQuMjM2TDQ4LjAxMTQgMTU0LjU4NEM0Ny45MjAzIDE1NC43MjIgNDcuODA0NCAxNTQuODU0IDQ3LjY2MzggMTU0Ljk3OUM0Ny41MjMyIDE1NS4xMDQgNDcuMzUgMTU1LjIwNSA0Ny4xNDQzIDE1NS4yODNDNDYuOTQxMSAxNTUuMzYxIDQ2LjcwMDIgMTU1LjQgNDYuNDIxNiAxNTUuNFpNNDguOTg4NiAxNDkuMzIySDQ5LjcxNTJWMTU0LjUwMkw0OS42NTI3IDE1NS4zMjJINDguOTg4NlYxNDkuMzIyWk01Mi41NzA2IDE1My4xNzRWMTUzLjI1NkM1Mi41NzA2IDE1My41NjMgNTIuNTM0MiAxNTMuODQ4IDUyLjQ2MTMgMTU0LjExMUM1Mi4zODgzIDE1NC4zNzIgNTIuMjgxNiAxNTQuNTk4IDUyLjE0MDkgMTU0Ljc5MUM1Mi4wMDAzIDE1NC45ODQgNTEuODI4NCAxNTUuMTMzIDUxLjYyNTMgMTU1LjI0QzUxLjQyMjIgMTU1LjM0NyA1MS4xODkxIDE1NS40IDUwLjkyNjEgMTU1LjRDNTAuNjU3OSAxNTUuNCA1MC40MjIyIDE1NS4zNTUgNTAuMjE5MSAxNTUuMjY0QzUwLjAxODUgMTU1LjE3IDQ5Ljg0OTMgMTU1LjAzNiA0OS43MTEzIDE1NC44NjFDNDkuNTczMiAxNTQuNjg3IDQ5LjQ2MjYgMTU0LjQ3NiA0OS4zNzkyIDE1NC4yMjlDNDkuMjk4NSAxNTMuOTgxIDQ5LjI0MjUgMTUzLjcwMiA0OS4yMTEzIDE1My4zOTNWMTUzLjAzM0M0OS4yNDI1IDE1Mi43MjEgNDkuMjk4NSAxNTIuNDQxIDQ5LjM3OTIgMTUyLjE5M0M0OS40NjI2IDE1MS45NDYgNDkuNTczMiAxNTEuNzM1IDQ5LjcxMTMgMTUxLjU2MUM0OS44NDkzIDE1MS4zODMgNTAuMDE4NSAxNTEuMjQ5IDUwLjIxOTEgMTUxLjE1OEM1MC40MTk2IDE1MS4wNjQgNTAuNjUyNyAxNTEuMDE4IDUwLjkxODMgMTUxLjAxOEM1MS4xODM5IDE1MS4wMTggNTEuNDE5NiAxNTEuMDcgNTEuNjI1MyAxNTEuMTc0QzUxLjgzMSAxNTEuMjc1IDUyLjAwMjkgMTUxLjQyMSA1Mi4xNDA5IDE1MS42MTFDNTIuMjgxNiAxNTEuODAxIDUyLjM4ODMgMTUyLjAyOSA1Mi40NjEzIDE1Mi4yOTVDNTIuNTM0MiAxNTIuNTU4IDUyLjU3MDYgMTUyLjg1MSA1Mi41NzA2IDE1My4xNzRaTTUxLjg0NDEgMTUzLjI1NlYxNTMuMTc0QzUxLjg0NDEgMTUyLjk2MyA1MS44MjQ1IDE1Mi43NjUgNTEuNzg1NSAxNTIuNThDNTEuNzQ2NCAxNTIuMzkzIDUxLjY4MzkgMTUyLjIyOSA1MS41OTggMTUyLjA4OEM1MS41MTIgMTUxLjk0NSA1MS4zOTg4IDE1MS44MzMgNTEuMjU4MSAxNTEuNzUyQzUxLjExNzUgMTUxLjY2OSA1MC45NDQzIDE1MS42MjcgNTAuNzM4NiAxNTEuNjI3QzUwLjU1NjMgMTUxLjYyNyA1MC4zOTc1IDE1MS42NTggNTAuMjYyIDE1MS43MjFDNTAuMTI5MiAxNTEuNzgzIDUwLjAxNTkgMTUxLjg2OCA0OS45MjIyIDE1MS45NzVDNDkuODI4NCAxNTIuMDc5IDQ5Ljc1MTYgMTUyLjE5OSA0OS42OTE3IDE1Mi4zMzRDNDkuNjM0NCAxNTIuNDY3IDQ5LjU5MTUgMTUyLjYwNSA0OS41NjI4IDE1Mi43NDhWMTUzLjY4OUM0OS42MDQ1IDE1My44NzIgNDkuNjcyMiAxNTQuMDQ4IDQ5Ljc2NTkgMTU0LjIxN0M0OS44NjIzIDE1NC4zODMgNDkuOTg5OSAxNTQuNTIgNTAuMTQ4OCAxNTQuNjI3QzUwLjMxMDIgMTU0LjczNCA1MC41MDk0IDE1NC43ODcgNTAuNzQ2NCAxNTQuNzg3QzUwLjk0MTcgMTU0Ljc4NyA1MS4xMDg0IDE1NC43NDggNTEuMjQ2NCAxNTQuNjdDNTEuMzg3IDE1NC41ODkgNTEuNTAwMyAxNTQuNDc5IDUxLjU4NjMgMTU0LjMzOEM1MS42NzQ4IDE1NC4xOTcgNTEuNzM5OSAxNTQuMDM1IDUxLjc4MTYgMTUzLjg1QzUxLjgyMzIgMTUzLjY2NSA1MS44NDQxIDE1My40NjcgNTEuODQ0MSAxNTMuMjU2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDFfNDE4NF85NDUyNykiPgo8bGluZSB4MT0iNzUuODQ5OSIgeTE9IjE0Ny4wNzIiIHgyPSI3NS44NDk5IiB5Mj0iMTQ2LjI1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNNjcuOTA5IDE1Mi4wMjVWMTUyLjg5MkM2Ny45MDkgMTUzLjM1OCA2Ny44NjczIDE1My43NTIgNjcuNzg0IDE1NC4wNzJDNjcuNzAwNiAxNTQuMzkyIDY3LjU4MDggMTU0LjY1IDY3LjQyNDYgMTU0Ljg0NUM2Ny4yNjgzIDE1NS4wNDEgNjcuMDc5NSAxNTUuMTgzIDY2Ljg1ODIgMTU1LjI3MUM2Ni42Mzk0IDE1NS4zNTcgNjYuMzkyIDE1NS40IDY2LjExNiAxNTUuNEM2NS44OTcyIDE1NS40IDY1LjY5NTQgMTU1LjM3MyA2NS41MTA1IDE1NS4zMThDNjUuMzI1NiAxNTUuMjYzIDY1LjE1OSAxNTUuMTc2IDY1LjAxMDUgMTU1LjA1NkM2NC44NjQ3IDE1NC45MzQgNjQuNzM5NyAxNTQuNzc1IDY0LjYzNTUgMTU0LjU4QzY0LjUzMTQgMTU0LjM4NSA2NC40NTE5IDE1NC4xNDggNjQuMzk3MiAxNTMuODY5QzY0LjM0MjYgMTUzLjU5IDY0LjMxNTIgMTUzLjI2NSA2NC4zMTUyIDE1Mi44OTJWMTUyLjAyNUM2NC4zMTUyIDE1MS41NTkgNjQuMzU2OSAxNTEuMTY4IDY0LjQ0MDIgMTUwLjg1M0M2NC41MjYxIDE1MC41MzggNjQuNjQ3MiAxNTAuMjg2IDY0LjgwMzUgMTUwLjA5NUM2NC45NTk3IDE0OS45MDMgNjUuMTQ3MiAxNDkuNzY1IDY1LjM2NiAxNDkuNjgxQzY1LjU4NzMgMTQ5LjU5OCA2NS44MzQ3IDE0OS41NTYgNjYuMTA4MiAxNDkuNTU2QzY2LjMyOTUgMTQ5LjU1NiA2Ni41MzI3IDE0OS41ODQgNjYuNzE3NiAxNDkuNjM4QzY2LjkwNTEgMTQ5LjY5MSA2Ny4wNzE3IDE0OS43NzUgNjcuMjE3NiAxNDkuODkyQzY3LjM2MzQgMTUwLjAwNyA2Ny40ODcxIDE1MC4xNjEgNjcuNTg4NiAxNTAuMzUzQzY3LjY5MjggMTUwLjU0MyA2Ny43NzIyIDE1MC43NzYgNjcuODI2OSAxNTEuMDUyQzY3Ljg4MTYgMTUxLjMyOSA2Ny45MDkgMTUxLjY1MyA2Ny45MDkgMTUyLjAyNVpNNjcuMTgyNCAxNTMuMDFWMTUxLjkwNEM2Ny4xODI0IDE1MS42NDkgNjcuMTY2OCAxNTEuNDI1IDY3LjEzNTUgMTUxLjIzMkM2Ny4xMDY5IDE1MS4wMzcgNjcuMDYzOSAxNTAuODcgNjcuMDA2NiAxNTAuNzMyQzY2Ljk0OTMgMTUwLjU5NCA2Ni44NzY0IDE1MC40ODIgNjYuNzg3OSAxNTAuMzk2QzY2LjcwMTkgMTUwLjMxIDY2LjYwMTcgMTUwLjI0OCA2Ni40ODcxIDE1MC4yMDlDNjYuMzc1MSAxNTAuMTY3IDY2LjI0ODggMTUwLjE0NiA2Ni4xMDgyIDE1MC4xNDZDNjUuOTM2MyAxNTAuMTQ2IDY1Ljc4NCAxNTAuMTc5IDY1LjY1MTEgMTUwLjI0NEM2NS41MTgzIDE1MC4zMDYgNjUuNDA2NCAxNTAuNDA3IDY1LjMxNTIgMTUwLjU0NUM2NS4yMjY3IDE1MC42ODMgNjUuMTU5IDE1MC44NjQgNjUuMTEyMSAxNTEuMDg4QzY1LjA2NTIgMTUxLjMxMiA2NS4wNDE4IDE1MS41ODQgNjUuMDQxOCAxNTEuOTA0VjE1My4wMUM2NS4wNDE4IDE1My4yNjUgNjUuMDU2MSAxNTMuNDkgNjUuMDg0NyAxNTMuNjg1QzY1LjExNiAxNTMuODgxIDY1LjE2MTYgMTU0LjA1IDY1LjIyMTUgMTU0LjE5M0M2NS4yODE0IDE1NC4zMzQgNjUuMzU0MyAxNTQuNDUgNjUuNDQwMiAxNTQuNTQxQzY1LjUyNjEgMTU0LjYzMiA2NS42MjUxIDE1NC43IDY1LjczNzEgMTU0Ljc0NEM2NS44NTE3IDE1NC43ODYgNjUuOTc4IDE1NC44MDYgNjYuMTE2IDE1NC44MDZDNjYuMjkzMSAxNTQuODA2IDY2LjQ0OCAxNTQuNzczIDY2LjU4MDggMTU0LjcwNUM2Ni43MTM2IDE1NC42MzcgNjYuODI0MyAxNTQuNTMyIDY2LjkxMjkgMTU0LjM4OEM2Ny4wMDQgMTU0LjI0MyA2Ny4wNzE3IDE1NC4wNTYgNjcuMTE2IDE1My44M0M2Ny4xNjAzIDE1My42MDEgNjcuMTgyNCAxNTMuMzI3IDY3LjE4MjQgMTUzLjAxWk03Mi42NDc4IDE1NC43MjhWMTU1LjMyMkg2OC45MjUyVjE1NC44MDJMNzAuNzg4NSAxNTIuNzI4QzcxLjAxNzYgMTUyLjQ3MyA3MS4xOTQ3IDE1Mi4yNTcgNzEuMzE5NyAxNTIuMDhDNzEuNDQ3MyAxNTEuOSA3MS41MzU5IDE1MS43NCA3MS41ODUzIDE1MS41OTlDNzEuNjM3NCAxNTEuNDU2IDcxLjY2MzUgMTUxLjMxIDcxLjY2MzUgMTUxLjE2MkM3MS42NjM1IDE1MC45NzQgNzEuNjI0NCAxNTAuODA1IDcxLjU0NjMgMTUwLjY1NEM3MS40NzA4IDE1MC41IDcxLjM1ODggMTUwLjM3OCA3MS4yMTAzIDE1MC4yODdDNzEuMDYxOSAxNTAuMTk2IDcwLjg4MjIgMTUwLjE1IDcwLjY3MTMgMTUwLjE1QzcwLjQxODcgMTUwLjE1IDcwLjIwNzcgMTUwLjIgNzAuMDM4NSAxNTAuMjk5QzY5Ljg3MTggMTUwLjM5NSA2OS43NDY4IDE1MC41MyA2OS42NjM1IDE1MC43MDVDNjkuNTgwMSAxNTAuODc5IDY5LjUzODUgMTUxLjA4IDY5LjUzODUgMTUxLjMwNkg2OC44MTU4QzY4LjgxNTggMTUwLjk4NiA2OC44ODYxIDE1MC42OTMgNjkuMDI2NyAxNTAuNDI3QzY5LjE2NzQgMTUwLjE2MiA2OS4zNzU3IDE0OS45NTEgNjkuNjUxNyAxNDkuNzk1QzY5LjkyNzggMTQ5LjYzNiA3MC4yNjc2IDE0OS41NTYgNzAuNjcxMyAxNDkuNTU2QzcxLjAzMDcgMTQ5LjU1NiA3MS4zMzc5IDE0OS42MiA3MS41OTMyIDE0OS43NDhDNzEuODQ4NCAxNDkuODczIDcyLjA0MzcgMTUwLjA1IDcyLjE3OTEgMTUwLjI3OUM3Mi4zMTcxIDE1MC41MDYgNzIuMzg2MSAxNTAuNzcxIDcyLjM4NjEgMTUxLjA3NkM3Mi4zODYxIDE1MS4yNDMgNzIuMzU3NSAxNTEuNDEyIDcyLjMwMDIgMTUxLjU4NEM3Mi4yNDU1IDE1MS43NTMgNzIuMTY4NyAxNTEuOTIyIDcyLjA2OTcgMTUyLjA5MkM3MS45NzM0IDE1Mi4yNjEgNzEuODYwMSAxNTIuNDI3IDcxLjcyOTkgMTUyLjU5MkM3MS42MDIzIDE1Mi43NTYgNzEuNDY1NSAxNTIuOTE3IDcxLjMxOTcgMTUzLjA3Nkw2OS43OTYzIDE1NC43MjhINzIuNjQ3OFpNNzYuNTEyMyAxNDkuNjM1VjE1NS4zMjJINzUuNzU4NFYxNDkuNjM1SDc2LjUxMjNaTTc4Ljg5NTEgMTUyLjE5M1YxNTIuODFINzYuMzQ4MlYxNTIuMTkzSDc4Ljg5NTFaTTc5LjI4MTggMTQ5LjYzNVYxNTAuMjUySDc2LjM0ODJWMTQ5LjYzNUg3OS4yODE4Wk04MS44MjE1IDE1NS40QzgxLjUyNzIgMTU1LjQgODEuMjYwMyAxNTUuMzUxIDgxLjAyMDcgMTU1LjI1MkM4MC43ODM3IDE1NS4xNSA4MC41NzkzIDE1NS4wMDggODAuNDA3NCAxNTQuODI2QzgwLjIzODIgMTU0LjY0NCA4MC4xMDggMTU0LjQyNyA4MC4wMTY4IDE1NC4xNzdDNzkuOTI1NyAxNTMuOTI3IDc5Ljg4MDEgMTUzLjY1NCA3OS44ODAxIDE1My4zNTdWMTUzLjE5M0M3OS44ODAxIDE1Mi44NDkgNzkuOTMwOSAxNTIuNTQzIDgwLjAzMjQgMTUyLjI3NUM4MC4xMzQgMTUyLjAwNCA4MC4yNzIgMTUxLjc3NSA4MC40NDY1IDE1MS41ODhDODAuNjIxIDE1MS40IDgwLjgxODkgMTUxLjI1OCA4MS4wNDAyIDE1MS4xNjJDODEuMjYxNiAxNTEuMDY2IDgxLjQ5MDggMTUxLjAxNyA4MS43Mjc3IDE1MS4wMTdDODIuMDI5OCAxNTEuMDE3IDgyLjI5MDIgMTUxLjA2OSA4Mi41MDkgMTUxLjE3NEM4Mi43MzA0IDE1MS4yNzggODIuOTExMyAxNTEuNDI0IDgzLjA1MiAxNTEuNjExQzgzLjE5MjYgMTUxLjc5NiA4My4yOTY4IDE1Mi4wMTUgODMuMzY0NSAxNTIuMjY3QzgzLjQzMjIgMTUyLjUxNyA4My40NjYgMTUyLjc5MSA4My40NjYgMTUzLjA4OFYxNTMuNDEySDgwLjMwOThWMTUyLjgyMkg4Mi43NDM0VjE1Mi43NjdDODIuNzMzIDE1Mi41OCA4Mi42OTM5IDE1Mi4zOTggODIuNjI2MiAxNTIuMjJDODIuNTYxMSAxNTIuMDQzIDgyLjQ1NjkgMTUxLjg5OCA4Mi4zMTM3IDE1MS43ODNDODIuMTcwNSAxNTEuNjY4IDgxLjk3NTEgMTUxLjYxMSA4MS43Mjc3IDE1MS42MTFDODEuNTYzNyAxNTEuNjExIDgxLjQxMjYgMTUxLjY0NiA4MS4yNzQ2IDE1MS43MTdDODEuMTM2NiAxNTEuNzg0IDgxLjAxODEgMTUxLjg4NiA4MC45MTkyIDE1Mi4wMjFDODAuODIwMiAxNTIuMTU3IDgwLjc0MzQgMTUyLjMyMiA4MC42ODg3IDE1Mi41MTdDODAuNjM0IDE1Mi43MTMgODAuNjA2NyAxNTIuOTM4IDgwLjYwNjcgMTUzLjE5M1YxNTMuMzU3QzgwLjYwNjcgMTUzLjU1OCA4MC42MzQgMTUzLjc0NyA4MC42ODg3IDE1My45MjRDODAuNzQ2IDE1NC4wOTggODAuODI4IDE1NC4yNTIgODAuOTM0OCAxNTQuMzg1QzgxLjA0NDIgMTU0LjUxNyA4MS4xNzU3IDE1NC42MjIgODEuMzI5MyAxNTQuNjk3QzgxLjQ4NTYgMTU0Ljc3MyA4MS42NjI2IDE1NC44MSA4MS44NjA2IDE1NC44MUM4Mi4xMTU4IDE1NC44MSA4Mi4zMzE5IDE1NC43NTggODIuNTA5IDE1NC42NTRDODIuNjg2MSAxNTQuNTUgODIuODQxIDE1NC40MTEgODIuOTczOCAxNTQuMjM2TDgzLjQxMTMgMTU0LjU4NEM4My4zMjAyIDE1NC43MjIgODMuMjA0MyAxNTQuODUzIDgzLjA2MzcgMTU0Ljk3OEM4Mi45MjMxIDE1NS4xMDMgODIuNzQ5OSAxNTUuMjA1IDgyLjU0NDIgMTU1LjI4M0M4Mi4zNDEgMTU1LjM2MSA4Mi4xMDAxIDE1NS40IDgxLjgyMTUgMTU1LjRaTTg0LjM4ODUgMTQ5LjMyMkg4NS4xMTUxVjE1NC41MDJMODUuMDUyNiAxNTUuMzIySDg0LjM4ODVWMTQ5LjMyMlpNODcuOTcwNSAxNTMuMTc0VjE1My4yNTZDODcuOTcwNSAxNTMuNTYzIDg3LjkzNDEgMTUzLjg0OCA4Ny44NjEyIDE1NC4xMTFDODcuNzg4MiAxNTQuMzcyIDg3LjY4MTUgMTU0LjU5OCA4Ny41NDA4IDE1NC43OTFDODcuNDAwMiAxNTQuOTgzIDg3LjIyODMgMTU1LjEzMyA4Ny4wMjUyIDE1NS4yNEM4Ni44MjIxIDE1NS4zNDcgODYuNTg5IDE1NS40IDg2LjMyNiAxNTUuNEM4Ni4wNTc4IDE1NS40IDg1LjgyMjEgMTU1LjM1NSA4NS42MTkgMTU1LjI2M0M4NS40MTg1IDE1NS4xNyA4NS4yNDkyIDE1NS4wMzYgODUuMTExMiAxNTQuODYxQzg0Ljk3MzEgMTU0LjY4NyA4NC44NjI1IDE1NC40NzYgODQuNzc5MSAxNTQuMjI4Qzg0LjY5ODQgMTUzLjk4MSA4NC42NDI0IDE1My43MDIgODQuNjExMiAxNTMuMzkyVjE1My4wMzNDODQuNjQyNCAxNTIuNzIgODQuNjk4NCAxNTIuNDQxIDg0Ljc3OTEgMTUyLjE5M0M4NC44NjI1IDE1MS45NDYgODQuOTczMSAxNTEuNzM1IDg1LjExMTIgMTUxLjU2Qzg1LjI0OTIgMTUxLjM4MyA4NS40MTg1IDE1MS4yNDkgODUuNjE5IDE1MS4xNThDODUuODE5NSAxNTEuMDY0IDg2LjA1MjYgMTUxLjAxNyA4Ni4zMTgyIDE1MS4wMTdDODYuNTgzOCAxNTEuMDE3IDg2LjgxOTUgMTUxLjA2OSA4Ny4wMjUyIDE1MS4xNzRDODcuMjMxIDE1MS4yNzUgODcuNDAyOCAxNTEuNDIxIDg3LjU0MDggMTUxLjYxMUM4Ny42ODE1IDE1MS44MDEgODcuNzg4MiAxNTIuMDI5IDg3Ljg2MTIgMTUyLjI5NUM4Ny45MzQxIDE1Mi41NTggODcuOTcwNSAxNTIuODUxIDg3Ljk3MDUgMTUzLjE3NFpNODcuMjQ0IDE1My4yNTZWMTUzLjE3NEM4Ny4yNDQgMTUyLjk2MyA4Ny4yMjQ0IDE1Mi43NjUgODcuMTg1NCAxNTIuNThDODcuMTQ2MyAxNTIuMzkyIDg3LjA4MzggMTUyLjIyOCA4Ni45OTc5IDE1Mi4wODhDODYuOTExOSAxNTEuOTQ0IDg2Ljc5ODcgMTUxLjgzMiA4Ni42NTggMTUxLjc1MkM4Ni41MTc0IDE1MS42NjggODYuMzQ0MiAxNTEuNjI3IDg2LjEzODUgMTUxLjYyN0M4NS45NTYyIDE1MS42MjcgODUuNzk3NCAxNTEuNjU4IDg1LjY2MTkgMTUxLjcyQzg1LjUyOTEgMTUxLjc4MyA4NS40MTU4IDE1MS44NjggODUuMzIyMSAxNTEuOTc0Qzg1LjIyODMgMTUyLjA3OSA4NS4xNTE1IDE1Mi4xOTggODUuMDkxNiAxNTIuMzM0Qzg1LjAzNDMgMTUyLjQ2NyA4NC45OTE0IDE1Mi42MDUgODQuOTYyNyAxNTIuNzQ4VjE1My42ODlDODUuMDA0NCAxNTMuODcyIDg1LjA3MjEgMTU0LjA0NyA4NS4xNjU4IDE1NC4yMTdDODUuMjYyMiAxNTQuMzgzIDg1LjM4OTggMTU0LjUyIDg1LjU0ODcgMTU0LjYyN0M4NS43MTAxIDE1NC43MzMgODUuOTA5MyAxNTQuNzg3IDg2LjE0NjMgMTU0Ljc4N0M4Ni4zNDE2IDE1NC43ODcgODYuNTA4MyAxNTQuNzQ4IDg2LjY0NjMgMTU0LjY3Qzg2Ljc4NjkgMTU0LjU4OSA4Ni45MDAyIDE1NC40NzggODYuOTg2MiAxNTQuMzM4Qzg3LjA3NDcgMTU0LjE5NyA4Ny4xMzk4IDE1NC4wMzQgODcuMTgxNSAxNTMuODQ5Qzg3LjIyMzEgMTUzLjY2NCA4Ny4yNDQgMTUzLjQ2NyA4Ny4yNDQgMTUzLjI1NloiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPC9nPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDJfNDE4NF85NDUyNykiPgo8bGluZSB4MT0iMTExLjI1IiB5MT0iMTQ3LjA3MiIgeDI9IjExMS4yNSIgeTI9IjE0Ni4yNSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNSIgc3Ryb2tlLXdpZHRoPSIwLjUiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPHBhdGggZD0iTTEwMy4zMDkgMTUyLjAyNVYxNTIuODkyQzEwMy4zMDkgMTUzLjM1OCAxMDMuMjY3IDE1My43NTIgMTAzLjE4NCAxNTQuMDcyQzEwMy4xMDEgMTU0LjM5MiAxMDIuOTgxIDE1NC42NSAxMDIuODI1IDE1NC44NDVDMTAyLjY2OCAxNTUuMDQxIDEwMi40OCAxNTUuMTgzIDEwMi4yNTggMTU1LjI3MUMxMDIuMDQgMTU1LjM1NyAxMDEuNzkyIDE1NS40IDEwMS41MTYgMTU1LjRDMTAxLjI5NyAxNTUuNCAxMDEuMDk2IDE1NS4zNzMgMTAwLjkxMSAxNTUuMzE4QzEwMC43MjYgMTU1LjI2MyAxMDAuNTU5IDE1NS4xNzYgMTAwLjQxMSAxNTUuMDU2QzEwMC4yNjUgMTU0LjkzNCAxMDAuMTQgMTU0Ljc3NSAxMDAuMDM2IDE1NC41OEM5OS45MzE1IDE1NC4zODUgOTkuODUyMSAxNTQuMTQ4IDk5Ljc5NzQgMTUzLjg2OUM5OS43NDI3IDE1My41OSA5OS43MTU0IDE1My4yNjUgOTkuNzE1NCAxNTIuODkyVjE1Mi4wMjVDOTkuNzE1NCAxNTEuNTU5IDk5Ljc1NyAxNTEuMTY4IDk5Ljg0MDQgMTUwLjg1M0M5OS45MjYzIDE1MC41MzggMTAwLjA0NyAxNTAuMjg2IDEwMC4yMDQgMTUwLjA5NUMxMDAuMzYgMTQ5LjkwMyAxMDAuNTQ3IDE0OS43NjUgMTAwLjc2NiAxNDkuNjgxQzEwMC45ODcgMTQ5LjU5OCAxMDEuMjM1IDE0OS41NTYgMTAxLjUwOCAxNDkuNTU2QzEwMS43MyAxNDkuNTU2IDEwMS45MzMgMTQ5LjU4NCAxMDIuMTE4IDE0OS42MzhDMTAyLjMwNSAxNDkuNjkxIDEwMi40NzIgMTQ5Ljc3NSAxMDIuNjE4IDE0OS44OTJDMTAyLjc2NCAxNTAuMDA3IDEwMi44ODcgMTUwLjE2MSAxMDIuOTg5IDE1MC4zNTNDMTAzLjA5MyAxNTAuNTQzIDEwMy4xNzIgMTUwLjc3NiAxMDMuMjI3IDE1MS4wNTJDMTAzLjI4MiAxNTEuMzI5IDEwMy4zMDkgMTUxLjY1MyAxMDMuMzA5IDE1Mi4wMjVaTTEwMi41ODMgMTUzLjAxVjE1MS45MDRDMTAyLjU4MyAxNTEuNjQ5IDEwMi41NjcgMTUxLjQyNSAxMDIuNTM2IDE1MS4yMzJDMTAyLjUwNyAxNTEuMDM3IDEwMi40NjQgMTUwLjg3IDEwMi40MDcgMTUwLjczMkMxMDIuMzQ5IDE1MC41OTQgMTAyLjI3NyAxNTAuNDgyIDEwMi4xODggMTUwLjM5NkMxMDIuMTAyIDE1MC4zMSAxMDIuMDAyIDE1MC4yNDggMTAxLjg4NyAxNTAuMjA5QzEwMS43NzUgMTUwLjE2NyAxMDEuNjQ5IDE1MC4xNDYgMTAxLjUwOCAxNTAuMTQ2QzEwMS4zMzYgMTUwLjE0NiAxMDEuMTg0IDE1MC4xNzkgMTAxLjA1MSAxNTAuMjQ0QzEwMC45MTggMTUwLjMwNiAxMDAuODA3IDE1MC40MDcgMTAwLjcxNSAxNTAuNTQ1QzEwMC42MjcgMTUwLjY4MyAxMDAuNTU5IDE1MC44NjQgMTAwLjUxMiAxNTEuMDg4QzEwMC40NjUgMTUxLjMxMiAxMDAuNDQyIDE1MS41ODQgMTAwLjQ0MiAxNTEuOTA0VjE1My4wMUMxMDAuNDQyIDE1My4yNjUgMTAwLjQ1NiAxNTMuNDkgMTAwLjQ4NSAxNTMuNjg1QzEwMC41MTYgMTUzLjg4MSAxMDAuNTYyIDE1NC4wNSAxMDAuNjIyIDE1NC4xOTNDMTAwLjY4MiAxNTQuMzM0IDEwMC43NTQgMTU0LjQ1IDEwMC44NCAxNTQuNTQxQzEwMC45MjYgMTU0LjYzMiAxMDEuMDI1IDE1NC43IDEwMS4xMzcgMTU0Ljc0NEMxMDEuMjUyIDE1NC43ODYgMTAxLjM3OCAxNTQuODA2IDEwMS41MTYgMTU0LjgwNkMxMDEuNjkzIDE1NC44MDYgMTAxLjg0OCAxNTQuNzczIDEwMS45ODEgMTU0LjcwNUMxMDIuMTE0IDE1NC42MzcgMTAyLjIyNCAxNTQuNTMyIDEwMi4zMTMgMTU0LjM4OEMxMDIuNDA0IDE1NC4yNDMgMTAyLjQ3MiAxNTQuMDU2IDEwMi41MTYgMTUzLjgzQzEwMi41NiAxNTMuNjAxIDEwMi41ODMgMTUzLjMyNyAxMDIuNTgzIDE1My4wMVpNMTA1LjM3NiAxNTIuMTIzSDEwNS44OTJDMTA2LjE0NCAxNTIuMTIzIDEwNi4zNTMgMTUyLjA4MSAxMDYuNTE3IDE1MS45OThDMTA2LjY4MyAxNTEuOTEyIDEwNi44MDcgMTUxLjc5NiAxMDYuODg4IDE1MS42NUMxMDYuOTcxIDE1MS41MDIgMTA3LjAxMyAxNTEuMzM1IDEwNy4wMTMgMTUxLjE1QzEwNy4wMTMgMTUwLjkzMSAxMDYuOTc2IDE1MC43NDggMTA2LjkwMyAxNTAuNTk5QzEwNi44MzEgMTUwLjQ1MSAxMDYuNzIxIDE1MC4zMzkgMTA2LjU3NSAxNTAuMjYzQzEwNi40MjkgMTUwLjE4OCAxMDYuMjQ1IDE1MC4xNSAxMDYuMDIxIDE1MC4xNUMxMDUuODE4IDE1MC4xNSAxMDUuNjM4IDE1MC4xOTEgMTA1LjQ4MiAxNTAuMjcxQzEwNS4zMjggMTUwLjM0OSAxMDUuMjA3IDE1MC40NjEgMTA1LjExOCAxNTAuNjA3QzEwNS4wMzIgMTUwLjc1MyAxMDQuOTg5IDE1MC45MjUgMTA0Ljk4OSAxNTEuMTIzSDEwNC4yNjdDMTA0LjI2NyAxNTAuODM0IDEwNC4zNCAxNTAuNTcxIDEwNC40ODUgMTUwLjMzNEMxMDQuNjMxIDE1MC4wOTcgMTA0LjgzNiAxNDkuOTA4IDEwNS4wOTkgMTQ5Ljc2N0MxMDUuMzY0IDE0OS42MjcgMTA1LjY3MiAxNDkuNTU2IDEwNi4wMjEgMTQ5LjU1NkMxMDYuMzY0IDE0OS41NTYgMTA2LjY2NSAxNDkuNjE4IDEwNi45MjMgMTQ5Ljc0QzEwNy4xODEgMTQ5Ljg2IDEwNy4zODEgMTUwLjAzOSAxMDcuNTI1IDE1MC4yNzlDMTA3LjY2OCAxNTAuNTE2IDEwNy43MzkgMTUwLjgxMiAxMDcuNzM5IDE1MS4xNjZDMTA3LjczOSAxNTEuMzA5IDEwNy43MDYgMTUxLjQ2MyAxMDcuNjM4IDE1MS42MjdDMTA3LjU3MyAxNTEuNzg4IDEwNy40NyAxNTEuOTM5IDEwNy4zMjkgMTUyLjA4QzEwNy4xOTEgMTUyLjIyIDEwNy4wMTIgMTUyLjMzNiAxMDYuNzkgMTUyLjQyN0MxMDYuNTY5IDE1Mi41MTYgMTA2LjMwMyAxNTIuNTYgMTA1Ljk5MyAxNTIuNTZIMTA1LjM3NlYxNTIuMTIzWk0xMDUuMzc2IDE1Mi43MTdWMTUyLjI4M0gxMDUuOTkzQzEwNi4zNTUgMTUyLjI4MyAxMDYuNjU1IDE1Mi4zMjYgMTA2Ljg5MiAxNTIuNDEyQzEwNy4xMjkgMTUyLjQ5OCAxMDcuMzE1IDE1Mi42MTIgMTA3LjQ1IDE1Mi43NTZDMTA3LjU4OCAxNTIuODk5IDEwNy42ODUgMTUzLjA1NiAxMDcuNzM5IDE1My4yMjhDMTA3Ljc5NyAxNTMuMzk4IDEwNy44MjUgMTUzLjU2NyAxMDcuODI1IDE1My43MzZDMTA3LjgyNSAxNTQuMDAyIDEwNy43OCAxNTQuMjM3IDEwNy42ODkgMTU0LjQ0M0MxMDcuNiAxNTQuNjQ5IDEwNy40NzQgMTU0LjgyMyAxMDcuMzEgMTU0Ljk2N0MxMDcuMTQ4IDE1NS4xMSAxMDYuOTU4IDE1NS4yMTggMTA2LjczOSAxNTUuMjkxQzEwNi41MjEgMTU1LjM2NCAxMDYuMjgyIDE1NS40IDEwNi4wMjUgMTU1LjRDMTA1Ljc3NyAxNTUuNCAxMDUuNTQ0IDE1NS4zNjUgMTA1LjMyNSAxNTUuMjk1QzEwNS4xMDkgMTU1LjIyNCAxMDQuOTE4IDE1NS4xMjMgMTA0Ljc1MSAxNTQuOTlDMTA0LjU4NCAxNTQuODU1IDEwNC40NTQgMTU0LjY4OSAxMDQuMzYgMTU0LjQ5NEMxMDQuMjY3IDE1NC4yOTYgMTA0LjIyIDE1NC4wNzEgMTA0LjIyIDE1My44MThIMTA0Ljk0M0MxMDQuOTQzIDE1NC4wMTYgMTA0Ljk4NSAxNTQuMTg5IDEwNS4wNzEgMTU0LjMzOEMxMDUuMTYgMTU0LjQ4NiAxMDUuMjg1IDE1NC42MDIgMTA1LjQ0NiAxNTQuNjg1QzEwNS42MSAxNTQuNzY2IDEwNS44MDMgMTU0LjgwNiAxMDYuMDI1IDE1NC44MDZDMTA2LjI0NiAxNTQuODA2IDEwNi40MzYgMTU0Ljc2OSAxMDYuNTk1IDE1NC42OTNDMTA2Ljc1NiAxNTQuNjE1IDEwNi44OCAxNTQuNDk4IDEwNi45NjYgMTU0LjM0MkMxMDcuMDU0IDE1NC4xODUgMTA3LjA5OSAxNTMuOTg5IDEwNy4wOTkgMTUzLjc1MkMxMDcuMDk5IDE1My41MTUgMTA3LjA0OSAxNTMuMzIxIDEwNi45NSAxNTMuMTdDMTA2Ljg1MSAxNTMuMDE2IDEwNi43MTEgMTUyLjkwMyAxMDYuNTI4IDE1Mi44M0MxMDYuMzQ5IDE1Mi43NTQgMTA2LjEzNyAxNTIuNzE3IDEwNS44OTIgMTUyLjcxN0gxMDUuMzc2Wk0xMTEuOTEyIDE0OS42MzVWMTU1LjMyMkgxMTEuMTU5VjE0OS42MzVIMTExLjkxMlpNMTE0LjI5NSAxNTIuMTkzVjE1Mi44MUgxMTEuNzQ4VjE1Mi4xOTNIMTE0LjI5NVpNMTE0LjY4MiAxNDkuNjM1VjE1MC4yNTJIMTExLjc0OFYxNDkuNjM1SDExNC42ODJaTTExNy4yMjIgMTU1LjRDMTE2LjkyNyAxNTUuNCAxMTYuNjYgMTU1LjM1MSAxMTYuNDIxIDE1NS4yNTJDMTE2LjE4NCAxNTUuMTUgMTE1Ljk3OSAxNTUuMDA4IDExNS44MDggMTU0LjgyNkMxMTUuNjM4IDE1NC42NDQgMTE1LjUwOCAxNTQuNDI3IDExNS40MTcgMTU0LjE3N0MxMTUuMzI2IDE1My45MjcgMTE1LjI4IDE1My42NTQgMTE1LjI4IDE1My4zNTdWMTUzLjE5M0MxMTUuMjggMTUyLjg0OSAxMTUuMzMxIDE1Mi41NDMgMTE1LjQzMyAxNTIuMjc1QzExNS41MzQgMTUyLjAwNCAxMTUuNjcyIDE1MS43NzUgMTE1Ljg0NyAxNTEuNTg4QzExNi4wMjEgMTUxLjQgMTE2LjIxOSAxNTEuMjU4IDExNi40NCAxNTEuMTYyQzExNi42NjIgMTUxLjA2NiAxMTYuODkxIDE1MS4wMTcgMTE3LjEyOCAxNTEuMDE3QzExNy40MyAxNTEuMDE3IDExNy42OSAxNTEuMDY5IDExNy45MDkgMTUxLjE3NEMxMTguMTMgMTUxLjI3OCAxMTguMzExIDE1MS40MjQgMTE4LjQ1MiAxNTEuNjExQzExOC41OTMgMTUxLjc5NiAxMTguNjk3IDE1Mi4wMTUgMTE4Ljc2NSAxNTIuMjY3QzExOC44MzIgMTUyLjUxNyAxMTguODY2IDE1Mi43OTEgMTE4Ljg2NiAxNTMuMDg4VjE1My40MTJIMTE1LjcxVjE1Mi44MjJIMTE4LjE0NFYxNTIuNzY3QzExOC4xMzMgMTUyLjU4IDExOC4wOTQgMTUyLjM5OCAxMTguMDI2IDE1Mi4yMkMxMTcuOTYxIDE1Mi4wNDMgMTE3Ljg1NyAxNTEuODk4IDExNy43MTQgMTUxLjc4M0MxMTcuNTcxIDE1MS42NjggMTE3LjM3NSAxNTEuNjExIDExNy4xMjggMTUxLjYxMUMxMTYuOTY0IDE1MS42MTEgMTE2LjgxMyAxNTEuNjQ2IDExNi42NzUgMTUxLjcxN0MxMTYuNTM3IDE1MS43ODQgMTE2LjQxOCAxNTEuODg2IDExNi4zMTkgMTUyLjAyMUMxMTYuMjIgMTUyLjE1NyAxMTYuMTQ0IDE1Mi4zMjIgMTE2LjA4OSAxNTIuNTE3QzExNi4wMzQgMTUyLjcxMyAxMTYuMDA3IDE1Mi45MzggMTE2LjAwNyAxNTMuMTkzVjE1My4zNTdDMTE2LjAwNyAxNTMuNTU4IDExNi4wMzQgMTUzLjc0NyAxMTYuMDg5IDE1My45MjRDMTE2LjE0NiAxNTQuMDk4IDExNi4yMjggMTU0LjI1MiAxMTYuMzM1IDE1NC4zODVDMTE2LjQ0NCAxNTQuNTE3IDExNi41NzYgMTU0LjYyMiAxMTYuNzI5IDE1NC42OTdDMTE2Ljg4NiAxNTQuNzczIDExNy4wNjMgMTU0LjgxIDExNy4yNjEgMTU0LjgxQzExNy41MTYgMTU0LjgxIDExNy43MzIgMTU0Ljc1OCAxMTcuOTA5IDE1NC42NTRDMTE4LjA4NiAxNTQuNTUgMTE4LjI0MSAxNTQuNDExIDExOC4zNzQgMTU0LjIzNkwxMTguODExIDE1NC41ODRDMTE4LjcyIDE1NC43MjIgMTE4LjYwNCAxNTQuODUzIDExOC40NjQgMTU0Ljk3OEMxMTguMzIzIDE1NS4xMDMgMTE4LjE1IDE1NS4yMDUgMTE3Ljk0NCAxNTUuMjgzQzExNy43NDEgMTU1LjM2MSAxMTcuNSAxNTUuNCAxMTcuMjIyIDE1NS40Wk0xMTkuNzg5IDE0OS4zMjJIMTIwLjUxNVYxNTQuNTAyTDEyMC40NTMgMTU1LjMyMkgxMTkuNzg5VjE0OS4zMjJaTTEyMy4zNzEgMTUzLjE3NFYxNTMuMjU2QzEyMy4zNzEgMTUzLjU2MyAxMjMuMzM0IDE1My44NDggMTIzLjI2MSAxNTQuMTExQzEyMy4xODggMTU0LjM3MiAxMjMuMDgyIDE1NC41OTggMTIyLjk0MSAxNTQuNzkxQzEyMi44IDE1NC45ODMgMTIyLjYyOCAxNTUuMTMzIDEyMi40MjUgMTU1LjI0QzEyMi4yMjIgMTU1LjM0NyAxMjEuOTg5IDE1NS40IDEyMS43MjYgMTU1LjRDMTIxLjQ1OCAxNTUuNCAxMjEuMjIyIDE1NS4zNTUgMTIxLjAxOSAxNTUuMjYzQzEyMC44MTkgMTU1LjE3IDEyMC42NDkgMTU1LjAzNiAxMjAuNTExIDE1NC44NjFDMTIwLjM3MyAxNTQuNjg3IDEyMC4yNjMgMTU0LjQ3NiAxMjAuMTc5IDE1NC4yMjhDMTIwLjA5OSAxNTMuOTgxIDEyMC4wNDMgMTUzLjcwMiAxMjAuMDExIDE1My4zOTJWMTUzLjAzM0MxMjAuMDQzIDE1Mi43MiAxMjAuMDk5IDE1Mi40NDEgMTIwLjE3OSAxNTIuMTkzQzEyMC4yNjMgMTUxLjk0NiAxMjAuMzczIDE1MS43MzUgMTIwLjUxMSAxNTEuNTZDMTIwLjY0OSAxNTEuMzgzIDEyMC44MTkgMTUxLjI0OSAxMjEuMDE5IDE1MS4xNThDMTIxLjIyIDE1MS4wNjQgMTIxLjQ1MyAxNTEuMDE3IDEyMS43MTggMTUxLjAxN0MxMjEuOTg0IDE1MS4wMTcgMTIyLjIyIDE1MS4wNjkgMTIyLjQyNSAxNTEuMTc0QzEyMi42MzEgMTUxLjI3NSAxMjIuODAzIDE1MS40MjEgMTIyLjk0MSAxNTEuNjExQzEyMy4wODIgMTUxLjgwMSAxMjMuMTg4IDE1Mi4wMjkgMTIzLjI2MSAxNTIuMjk1QzEyMy4zMzQgMTUyLjU1OCAxMjMuMzcxIDE1Mi44NTEgMTIzLjM3MSAxNTMuMTc0Wk0xMjIuNjQ0IDE1My4yNTZWMTUzLjE3NEMxMjIuNjQ0IDE1Mi45NjMgMTIyLjYyNSAxNTIuNzY1IDEyMi41ODYgMTUyLjU4QzEyMi41NDYgMTUyLjM5MiAxMjIuNDg0IDE1Mi4yMjggMTIyLjM5OCAxNTIuMDg4QzEyMi4zMTIgMTUxLjk0NCAxMjIuMTk5IDE1MS44MzIgMTIyLjA1OCAxNTEuNzUyQzEyMS45MTggMTUxLjY2OCAxMjEuNzQ0IDE1MS42MjcgMTIxLjUzOSAxNTEuNjI3QzEyMS4zNTYgMTUxLjYyNyAxMjEuMTk4IDE1MS42NTggMTIxLjA2MiAxNTEuNzJDMTIwLjkyOSAxNTEuNzgzIDEyMC44MTYgMTUxLjg2OCAxMjAuNzIyIDE1MS45NzRDMTIwLjYyOCAxNTIuMDc5IDEyMC41NTIgMTUyLjE5OCAxMjAuNDkyIDE1Mi4zMzRDMTIwLjQzNCAxNTIuNDY3IDEyMC4zOTIgMTUyLjYwNSAxMjAuMzYzIDE1Mi43NDhWMTUzLjY4OUMxMjAuNDA1IDE1My44NzIgMTIwLjQ3MiAxNTQuMDQ3IDEyMC41NjYgMTU0LjIxN0MxMjAuNjYyIDE1NC4zODMgMTIwLjc5IDE1NC41MiAxMjAuOTQ5IDE1NC42MjdDMTIxLjExIDE1NC43MzMgMTIxLjMwOSAxNTQuNzg3IDEyMS41NDYgMTU0Ljc4N0MxMjEuNzQyIDE1NC43ODcgMTIxLjkwOCAxNTQuNzQ4IDEyMi4wNDYgMTU0LjY3QzEyMi4xODcgMTU0LjU4OSAxMjIuMyAxNTQuNDc4IDEyMi4zODYgMTU0LjMzOEMxMjIuNDc1IDE1NC4xOTcgMTIyLjU0IDE1NC4wMzQgMTIyLjU4MiAxNTMuODQ5QzEyMi42MjMgMTUzLjY2NCAxMjIuNjQ0IDE1My40NjcgMTIyLjY0NCAxNTMuMjU2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8L2c+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwM180MTg0Xzk0NTI3KSI+CjxsaW5lIHgxPSIxNDYuNjUiIHkxPSIxNDcuMDcyIiB4Mj0iMTQ2LjY1IiB5Mj0iMTQ2LjI1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNMTM4LjcwOSAxNTIuMDI1VjE1Mi44OTJDMTM4LjcwOSAxNTMuMzU4IDEzOC42NjcgMTUzLjc1MiAxMzguNTg0IDE1NC4wNzJDMTM4LjUwMSAxNTQuMzkyIDEzOC4zODEgMTU0LjY1IDEzOC4yMjUgMTU0Ljg0NUMxMzguMDY4IDE1NS4wNDEgMTM3Ljg4IDE1NS4xODMgMTM3LjY1OCAxNTUuMjcxQzEzNy40MzkgMTU1LjM1NyAxMzcuMTkyIDE1NS40IDEzNi45MTYgMTU1LjRDMTM2LjY5NyAxNTUuNCAxMzYuNDk1IDE1NS4zNzMgMTM2LjMxMSAxNTUuMzE4QzEzNi4xMjYgMTU1LjI2MyAxMzUuOTU5IDE1NS4xNzYgMTM1LjgxMSAxNTUuMDU2QzEzNS42NjUgMTU0LjkzNCAxMzUuNTQgMTU0Ljc3NSAxMzUuNDM2IDE1NC41OEMxMzUuMzMxIDE1NC4zODUgMTM1LjI1MiAxNTQuMTQ4IDEzNS4xOTcgMTUzLjg2OUMxMzUuMTQzIDE1My41OSAxMzUuMTE1IDE1My4yNjUgMTM1LjExNSAxNTIuODkyVjE1Mi4wMjVDMTM1LjExNSAxNTEuNTU5IDEzNS4xNTcgMTUxLjE2OCAxMzUuMjQgMTUwLjg1M0MxMzUuMzI2IDE1MC41MzggMTM1LjQ0NyAxNTAuMjg2IDEzNS42MDQgMTUwLjA5NUMxMzUuNzYgMTQ5LjkwMyAxMzUuOTQ3IDE0OS43NjUgMTM2LjE2NiAxNDkuNjgxQzEzNi4zODcgMTQ5LjU5OCAxMzYuNjM1IDE0OS41NTYgMTM2LjkwOCAxNDkuNTU2QzEzNy4xMyAxNDkuNTU2IDEzNy4zMzMgMTQ5LjU4NCAxMzcuNTE4IDE0OS42MzhDMTM3LjcwNSAxNDkuNjkxIDEzNy44NzIgMTQ5Ljc3NSAxMzguMDE4IDE0OS44OTJDMTM4LjE2MyAxNTAuMDA3IDEzOC4yODcgMTUwLjE2MSAxMzguMzg5IDE1MC4zNTNDMTM4LjQ5MyAxNTAuNTQzIDEzOC41NzIgMTUwLjc3NiAxMzguNjI3IDE1MS4wNTJDMTM4LjY4MiAxNTEuMzI5IDEzOC43MDkgMTUxLjY1MyAxMzguNzA5IDE1Mi4wMjVaTTEzNy45ODIgMTUzLjAxVjE1MS45MDRDMTM3Ljk4MiAxNTEuNjQ5IDEzNy45NjcgMTUxLjQyNSAxMzcuOTM2IDE1MS4yMzJDMTM3LjkwNyAxNTEuMDM3IDEzNy44NjQgMTUwLjg3IDEzNy44MDcgMTUwLjczMkMxMzcuNzQ5IDE1MC41OTQgMTM3LjY3NiAxNTAuNDgyIDEzNy41ODggMTUwLjM5NkMxMzcuNTAyIDE1MC4zMSAxMzcuNDAyIDE1MC4yNDggMTM3LjI4NyAxNTAuMjA5QzEzNy4xNzUgMTUwLjE2NyAxMzcuMDQ5IDE1MC4xNDYgMTM2LjkwOCAxNTAuMTQ2QzEzNi43MzYgMTUwLjE0NiAxMzYuNTg0IDE1MC4xNzkgMTM2LjQ1MSAxNTAuMjQ0QzEzNi4zMTggMTUwLjMwNiAxMzYuMjA2IDE1MC40MDcgMTM2LjExNSAxNTAuNTQ1QzEzNi4wMjcgMTUwLjY4MyAxMzUuOTU5IDE1MC44NjQgMTM1LjkxMiAxNTEuMDg4QzEzNS44NjUgMTUxLjMxMiAxMzUuODQyIDE1MS41ODQgMTM1Ljg0MiAxNTEuOTA0VjE1My4wMUMxMzUuODQyIDE1My4yNjUgMTM1Ljg1NiAxNTMuNDkgMTM1Ljg4NSAxNTMuNjg1QzEzNS45MTYgMTUzLjg4MSAxMzUuOTYyIDE1NC4wNSAxMzYuMDIyIDE1NC4xOTNDMTM2LjA4MSAxNTQuMzM0IDEzNi4xNTQgMTU0LjQ1IDEzNi4yNCAxNTQuNTQxQzEzNi4zMjYgMTU0LjYzMiAxMzYuNDI1IDE1NC43IDEzNi41MzcgMTU0Ljc0NEMxMzYuNjUyIDE1NC43ODYgMTM2Ljc3OCAxNTQuODA2IDEzNi45MTYgMTU0LjgwNkMxMzcuMDkzIDE1NC44MDYgMTM3LjI0OCAxNTQuNzczIDEzNy4zODEgMTU0LjcwNUMxMzcuNTE0IDE1NC42MzcgMTM3LjYyNCAxNTQuNTMyIDEzNy43MTMgMTU0LjM4OEMxMzcuODA0IDE1NC4yNDMgMTM3Ljg3MiAxNTQuMDU2IDEzNy45MTYgMTUzLjgzQzEzNy45NiAxNTMuNjAxIDEzNy45ODIgMTUzLjMyNyAxMzcuOTgyIDE1My4wMVpNMTQzLjU2NSAxNTMuNDA4VjE1NC4wMDJIMTM5LjQ1NlYxNTMuNTc2TDE0Mi4wMDMgMTQ5LjYzNUgxNDIuNTkyTDE0MS45NiAxNTAuNzc1TDE0MC4yNzYgMTUzLjQwOEgxNDMuNTY1Wk0xNDIuNzcyIDE0OS42MzVWMTU1LjMyMkgxNDIuMDQ5VjE0OS42MzVIMTQyLjc3MlpNMTQ3LjMxMiAxNDkuNjM1VjE1NS4zMjJIMTQ2LjU1OFYxNDkuNjM1SDE0Ny4zMTJaTTE0OS42OTUgMTUyLjE5M1YxNTIuODFIMTQ3LjE0OFYxNTIuMTkzSDE0OS42OTVaTTE1MC4wODIgMTQ5LjYzNVYxNTAuMjUySDE0Ny4xNDhWMTQ5LjYzNUgxNTAuMDgyWk0xNTIuNjIyIDE1NS40QzE1Mi4zMjcgMTU1LjQgMTUyLjA2IDE1NS4zNTEgMTUxLjgyMSAxNTUuMjUyQzE1MS41ODQgMTU1LjE1IDE1MS4zNzkgMTU1LjAwOCAxNTEuMjA3IDE1NC44MjZDMTUxLjAzOCAxNTQuNjQ0IDE1MC45MDggMTU0LjQyNyAxNTAuODE3IDE1NC4xNzdDMTUwLjcyNiAxNTMuOTI3IDE1MC42OCAxNTMuNjU0IDE1MC42OCAxNTMuMzU3VjE1My4xOTNDMTUwLjY4IDE1Mi44NDkgMTUwLjczMSAxNTIuNTQzIDE1MC44MzIgMTUyLjI3NUMxNTAuOTM0IDE1Mi4wMDQgMTUxLjA3MiAxNTEuNzc1IDE1MS4yNDcgMTUxLjU4OEMxNTEuNDIxIDE1MS40IDE1MS42MTkgMTUxLjI1OCAxNTEuODQgMTUxLjE2MkMxNTIuMDYyIDE1MS4wNjYgMTUyLjI5MSAxNTEuMDE3IDE1Mi41MjggMTUxLjAxN0MxNTIuODMgMTUxLjAxNyAxNTMuMDkgMTUxLjA2OSAxNTMuMzA5IDE1MS4xNzRDMTUzLjUzIDE1MS4yNzggMTUzLjcxMSAxNTEuNDI0IDE1My44NTIgMTUxLjYxMUMxNTMuOTkzIDE1MS43OTYgMTU0LjA5NyAxNTIuMDE1IDE1NC4xNjUgMTUyLjI2N0MxNTQuMjMyIDE1Mi41MTcgMTU0LjI2NiAxNTIuNzkxIDE1NC4yNjYgMTUzLjA4OFYxNTMuNDEySDE1MS4xMVYxNTIuODIySDE1My41NDNWMTUyLjc2N0MxNTMuNTMzIDE1Mi41OCAxNTMuNDk0IDE1Mi4zOTggMTUzLjQyNiAxNTIuMjJDMTUzLjM2MSAxNTIuMDQzIDE1My4yNTcgMTUxLjg5OCAxNTMuMTE0IDE1MS43ODNDMTUyLjk3MSAxNTEuNjY4IDE1Mi43NzUgMTUxLjYxMSAxNTIuNTI4IDE1MS42MTFDMTUyLjM2NCAxNTEuNjExIDE1Mi4yMTMgMTUxLjY0NiAxNTIuMDc1IDE1MS43MTdDMTUxLjkzNyAxNTEuNzg0IDE1MS44MTggMTUxLjg4NiAxNTEuNzE5IDE1Mi4wMjFDMTUxLjYyIDE1Mi4xNTcgMTUxLjU0MyAxNTIuMzIyIDE1MS40ODkgMTUyLjUxN0MxNTEuNDM0IDE1Mi43MTMgMTUxLjQwNyAxNTIuOTM4IDE1MS40MDcgMTUzLjE5M1YxNTMuMzU3QzE1MS40MDcgMTUzLjU1OCAxNTEuNDM0IDE1My43NDcgMTUxLjQ4OSAxNTMuOTI0QzE1MS41NDYgMTU0LjA5OCAxNTEuNjI4IDE1NC4yNTIgMTUxLjczNSAxNTQuMzg1QzE1MS44NDQgMTU0LjUxNyAxNTEuOTc2IDE1NC42MjIgMTUyLjEyOSAxNTQuNjk3QzE1Mi4yODYgMTU0Ljc3MyAxNTIuNDYzIDE1NC44MSAxNTIuNjYxIDE1NC44MUMxNTIuOTE2IDE1NC44MSAxNTMuMTMyIDE1NC43NTggMTUzLjMwOSAxNTQuNjU0QzE1My40ODYgMTU0LjU1IDE1My42NDEgMTU0LjQxMSAxNTMuNzc0IDE1NC4yMzZMMTU0LjIxMSAxNTQuNTg0QzE1NC4xMiAxNTQuNzIyIDE1NC4wMDQgMTU0Ljg1MyAxNTMuODY0IDE1NC45NzhDMTUzLjcyMyAxNTUuMTAzIDE1My41NSAxNTUuMjA1IDE1My4zNDQgMTU1LjI4M0MxNTMuMTQxIDE1NS4zNjEgMTUyLjkgMTU1LjQgMTUyLjYyMiAxNTUuNFpNMTU1LjE4OSAxNDkuMzIySDE1NS45MTVWMTU0LjUwMkwxNTUuODUzIDE1NS4zMjJIMTU1LjE4OVYxNDkuMzIyWk0xNTguNzcxIDE1My4xNzRWMTUzLjI1NkMxNTguNzcxIDE1My41NjMgMTU4LjczNCAxNTMuODQ4IDE1OC42NjEgMTU0LjExMUMxNTguNTg4IDE1NC4zNzIgMTU4LjQ4MiAxNTQuNTk4IDE1OC4zNDEgMTU0Ljc5MUMxNTguMiAxNTQuOTgzIDE1OC4wMjggMTU1LjEzMyAxNTcuODI1IDE1NS4yNEMxNTcuNjIyIDE1NS4zNDcgMTU3LjM4OSAxNTUuNCAxNTcuMTI2IDE1NS40QzE1Ni44NTggMTU1LjQgMTU2LjYyMiAxNTUuMzU1IDE1Ni40MTkgMTU1LjI2M0MxNTYuMjE4IDE1NS4xNyAxNTYuMDQ5IDE1NS4wMzYgMTU1LjkxMSAxNTQuODYxQzE1NS43NzMgMTU0LjY4NyAxNTUuNjYzIDE1NC40NzYgMTU1LjU3OSAxNTQuMjI4QzE1NS40OTggMTUzLjk4MSAxNTUuNDQyIDE1My43MDIgMTU1LjQxMSAxNTMuMzkyVjE1My4wMzNDMTU1LjQ0MiAxNTIuNzIgMTU1LjQ5OCAxNTIuNDQxIDE1NS41NzkgMTUyLjE5M0MxNTUuNjYzIDE1MS45NDYgMTU1Ljc3MyAxNTEuNzM1IDE1NS45MTEgMTUxLjU2QzE1Ni4wNDkgMTUxLjM4MyAxNTYuMjE4IDE1MS4yNDkgMTU2LjQxOSAxNTEuMTU4QzE1Ni42MiAxNTEuMDY0IDE1Ni44NTMgMTUxLjAxNyAxNTcuMTE4IDE1MS4wMTdDMTU3LjM4NCAxNTEuMDE3IDE1Ny42MiAxNTEuMDY5IDE1Ny44MjUgMTUxLjE3NEMxNTguMDMxIDE1MS4yNzUgMTU4LjIwMyAxNTEuNDIxIDE1OC4zNDEgMTUxLjYxMUMxNTguNDgyIDE1MS44MDEgMTU4LjU4OCAxNTIuMDI5IDE1OC42NjEgMTUyLjI5NUMxNTguNzM0IDE1Mi41NTggMTU4Ljc3MSAxNTIuODUxIDE1OC43NzEgMTUzLjE3NFpNMTU4LjA0NCAxNTMuMjU2VjE1My4xNzRDMTU4LjA0NCAxNTIuOTYzIDE1OC4wMjQgMTUyLjc2NSAxNTcuOTg1IDE1Mi41OEMxNTcuOTQ2IDE1Mi4zOTIgMTU3Ljg4NCAxNTIuMjI4IDE1Ny43OTggMTUyLjA4OEMxNTcuNzEyIDE1MS45NDQgMTU3LjU5OSAxNTEuODMyIDE1Ny40NTggMTUxLjc1MkMxNTcuMzE3IDE1MS42NjggMTU3LjE0NCAxNTEuNjI3IDE1Ni45MzkgMTUxLjYyN0MxNTYuNzU2IDE1MS42MjcgMTU2LjU5NyAxNTEuNjU4IDE1Ni40NjIgMTUxLjcyQzE1Ni4zMjkgMTUxLjc4MyAxNTYuMjE2IDE1MS44NjggMTU2LjEyMiAxNTEuOTc0QzE1Ni4wMjggMTUyLjA3OSAxNTUuOTUyIDE1Mi4xOTggMTU1Ljg5MiAxNTIuMzM0QzE1NS44MzQgMTUyLjQ2NyAxNTUuNzkxIDE1Mi42MDUgMTU1Ljc2MyAxNTIuNzQ4VjE1My42ODlDMTU1LjgwNCAxNTMuODcyIDE1NS44NzIgMTU0LjA0NyAxNTUuOTY2IDE1NC4yMTdDMTU2LjA2MiAxNTQuMzgzIDE1Ni4xOSAxNTQuNTIgMTU2LjM0OSAxNTQuNjI3QzE1Ni41MSAxNTQuNzMzIDE1Ni43MDkgMTU0Ljc4NyAxNTYuOTQ2IDE1NC43ODdDMTU3LjE0MiAxNTQuNzg3IDE1Ny4zMDggMTU0Ljc0OCAxNTcuNDQ2IDE1NC42N0MxNTcuNTg3IDE1NC41ODkgMTU3LjcgMTU0LjQ3OCAxNTcuNzg2IDE1NC4zMzhDMTU3Ljg3NSAxNTQuMTk3IDE1Ny45NCAxNTQuMDM0IDE1Ny45ODIgMTUzLjg0OUMxNTguMDIzIDE1My42NjQgMTU4LjA0NCAxNTMuNDY3IDE1OC4wNDQgMTUzLjI1NloiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPC9nPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDRfNDE4NF85NDUyNykiPgo8bGluZSB4MT0iMTgyLjA1IiB5MT0iMTQ3LjA3MiIgeDI9IjE4Mi4wNSIgeTI9IjE0Ni4yNSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNSIgc3Ryb2tlLXdpZHRoPSIwLjUiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPHBhdGggZD0iTTE3NC4xMDkgMTUyLjAyNVYxNTIuODkyQzE3NC4xMDkgMTUzLjM1OCAxNzQuMDY3IDE1My43NTIgMTczLjk4NCAxNTQuMDcyQzE3My45MDEgMTU0LjM5MiAxNzMuNzgxIDE1NC42NSAxNzMuNjI1IDE1NC44NDVDMTczLjQ2OSAxNTUuMDQxIDE3My4yOCAxNTUuMTgzIDE3My4wNTggMTU1LjI3MUMxNzIuODQgMTU1LjM1NyAxNzIuNTkyIDE1NS40IDE3Mi4zMTYgMTU1LjRDMTcyLjA5NyAxNTUuNCAxNzEuODk2IDE1NS4zNzMgMTcxLjcxMSAxNTUuMzE4QzE3MS41MjYgMTU1LjI2MyAxNzEuMzU5IDE1NS4xNzYgMTcxLjIxMSAxNTUuMDU2QzE3MS4wNjUgMTU0LjkzNCAxNzAuOTQgMTU0Ljc3NSAxNzAuODM2IDE1NC41OEMxNzAuNzMyIDE1NC4zODUgMTcwLjY1MiAxNTQuMTQ4IDE3MC41OTcgMTUzLjg2OUMxNzAuNTQzIDE1My41OSAxNzAuNTE1IDE1My4yNjUgMTcwLjUxNSAxNTIuODkyVjE1Mi4wMjVDMTcwLjUxNSAxNTEuNTU5IDE3MC41NTcgMTUxLjE2OCAxNzAuNjQgMTUwLjg1M0MxNzAuNzI2IDE1MC41MzggMTcwLjg0NyAxNTAuMjg2IDE3MS4wMDQgMTUwLjA5NUMxNzEuMTYgMTQ5LjkwMyAxNzEuMzQ3IDE0OS43NjUgMTcxLjU2NiAxNDkuNjgxQzE3MS43ODggMTQ5LjU5OCAxNzIuMDM1IDE0OS41NTYgMTcyLjMwOCAxNDkuNTU2QzE3Mi41MyAxNDkuNTU2IDE3Mi43MzMgMTQ5LjU4NCAxNzIuOTE4IDE0OS42MzhDMTczLjEwNSAxNDkuNjkxIDE3My4yNzIgMTQ5Ljc3NSAxNzMuNDE4IDE0OS44OTJDMTczLjU2NCAxNTAuMDA3IDE3My42ODcgMTUwLjE2MSAxNzMuNzg5IDE1MC4zNTNDMTczLjg5MyAxNTAuNTQzIDE3My45NzIgMTUwLjc3NiAxNzQuMDI3IDE1MS4wNTJDMTc0LjA4MiAxNTEuMzI5IDE3NC4xMDkgMTUxLjY1MyAxNzQuMTA5IDE1Mi4wMjVaTTE3My4zODMgMTUzLjAxVjE1MS45MDRDMTczLjM4MyAxNTEuNjQ5IDE3My4zNjcgMTUxLjQyNSAxNzMuMzM2IDE1MS4yMzJDMTczLjMwNyAxNTEuMDM3IDE3My4yNjQgMTUwLjg3IDE3My4yMDcgMTUwLjczMkMxNzMuMTUgMTUwLjU5NCAxNzMuMDc3IDE1MC40ODIgMTcyLjk4OCAxNTAuMzk2QzE3Mi45MDIgMTUwLjMxIDE3Mi44MDIgMTUwLjI0OCAxNzIuNjg3IDE1MC4yMDlDMTcyLjU3NSAxNTAuMTY3IDE3Mi40NDkgMTUwLjE0NiAxNzIuMzA4IDE1MC4xNDZDMTcyLjEzNiAxNTAuMTQ2IDE3MS45ODQgMTUwLjE3OSAxNzEuODUxIDE1MC4yNDRDMTcxLjcxOSAxNTAuMzA2IDE3MS42MDcgMTUwLjQwNyAxNzEuNTE1IDE1MC41NDVDMTcxLjQyNyAxNTAuNjgzIDE3MS4zNTkgMTUwLjg2NCAxNzEuMzEyIDE1MS4wODhDMTcxLjI2NSAxNTEuMzEyIDE3MS4yNDIgMTUxLjU4NCAxNzEuMjQyIDE1MS45MDRWMTUzLjAxQzE3MS4yNDIgMTUzLjI2NSAxNzEuMjU2IDE1My40OSAxNzEuMjg1IDE1My42ODVDMTcxLjMxNiAxNTMuODgxIDE3MS4zNjIgMTU0LjA1IDE3MS40MjIgMTU0LjE5M0MxNzEuNDgyIDE1NC4zMzQgMTcxLjU1NCAxNTQuNDUgMTcxLjY0IDE1NC41NDFDMTcxLjcyNiAxNTQuNjMyIDE3MS44MjUgMTU0LjcgMTcxLjkzNyAxNTQuNzQ0QzE3Mi4wNTIgMTU0Ljc4NiAxNzIuMTc4IDE1NC44MDYgMTcyLjMxNiAxNTQuODA2QzE3Mi40OTMgMTU0LjgwNiAxNzIuNjQ4IDE1NC43NzMgMTcyLjc4MSAxNTQuNzA1QzE3Mi45MTQgMTU0LjYzNyAxNzMuMDI1IDE1NC41MzIgMTczLjExMyAxNTQuMzg4QzE3My4yMDQgMTU0LjI0MyAxNzMuMjcyIDE1NC4wNTYgMTczLjMxNiAxNTMuODNDMTczLjM2IDE1My42MDEgMTczLjM4MyAxNTMuMzI3IDE3My4zODMgMTUzLjAxWk0xNzYuMDM2IDE1Mi42MTVMMTc1LjQ1NyAxNTIuNDY3TDE3NS43NDMgMTQ5LjYzNUgxNzguNjYxVjE1MC4zMDJIMTc2LjM1NkwxNzYuMTg0IDE1MS44NDlDMTc2LjI4OCAxNTEuNzg5IDE3Ni40MiAxNTEuNzMzIDE3Ni41NzkgMTUxLjY4MUMxNzYuNzQgMTUxLjYyOSAxNzYuOTI1IDE1MS42MDMgMTc3LjEzMyAxNTEuNjAzQzE3Ny4zOTYgMTUxLjYwMyAxNzcuNjMyIDE1MS42NDkgMTc3Ljg0IDE1MS43NEMxNzguMDQ5IDE1MS44MjkgMTc4LjIyNiAxNTEuOTU2IDE3OC4zNzEgMTUyLjEyM0MxNzguNTIgMTUyLjI4OSAxNzguNjMzIDE1Mi40OSAxNzguNzExIDE1Mi43MjRDMTc4Ljc4OSAxNTIuOTU5IDE3OC44MjkgMTUzLjIyIDE3OC44MjkgMTUzLjUxQzE3OC44MjkgMTUzLjc4MyAxNzguNzkxIDE1NC4wMzQgMTc4LjcxNSAxNTQuMjYzQzE3OC42NDIgMTU0LjQ5MyAxNzguNTMyIDE1NC42OTMgMTc4LjM4MyAxNTQuODY1QzE3OC4yMzUgMTU1LjAzNCAxNzguMDQ3IDE1NS4xNjYgMTc3LjgyMSAxNTUuMjZDMTc3LjU5NyAxNTUuMzUzIDE3Ny4zMzIgMTU1LjQgMTc3LjAyOCAxNTUuNEMxNzYuNzk5IDE1NS40IDE3Ni41ODEgMTU1LjM2OSAxNzYuMzc1IDE1NS4zMDZDMTc2LjE3MiAxNTUuMjQxIDE3NS45OSAxNTUuMTQ0IDE3NS44MjkgMTU1LjAxM0MxNzUuNjcgMTU0Ljg4MSAxNzUuNTM5IDE1NC43MTcgMTc1LjQzOCAxNTQuNTIxQzE3NS4zMzkgMTU0LjMyMyAxNzUuMjc2IDE1NC4wOTIgMTc1LjI1IDE1My44MjZIMTc1LjkzOEMxNzUuOTY5IDE1NC4wMzkgMTc2LjAzMiAxNTQuMjE5IDE3Ni4xMjUgMTU0LjM2NUMxNzYuMjE5IDE1NC41MTEgMTc2LjM0MiAxNTQuNjIyIDE3Ni40OTMgMTU0LjY5N0MxNzYuNjQ2IDE1NC43NyAxNzYuODI1IDE1NC44MDYgMTc3LjAyOCAxNTQuODA2QzE3Ny4yIDE1NC44MDYgMTc3LjM1MiAxNTQuNzc2IDE3Ny40ODUgMTU0LjcxN0MxNzcuNjE4IDE1NC42NTcgMTc3LjczIDE1NC41NzEgMTc3LjgyMSAxNTQuNDU5QzE3Ny45MTIgMTU0LjM0NyAxNzcuOTgxIDE1NC4yMTEgMTc4LjAyOCAxNTQuMDUyQzE3OC4wNzcgMTUzLjg5NCAxNzguMTAyIDE1My43MTUgMTc4LjEwMiAxNTMuNTE3QzE3OC4xMDIgMTUzLjMzOCAxNzguMDc3IDE1My4xNzEgMTc4LjAyOCAxNTMuMDE3QzE3Ny45NzggMTUyLjg2NCAxNzcuOTA0IDE1Mi43MyAxNzcuODA1IDE1Mi42MTVDMTc3LjcwOSAxNTIuNSAxNzcuNTkgMTUyLjQxMiAxNzcuNDUgMTUyLjM0OUMxNzcuMzA5IDE1Mi4yODQgMTc3LjE0OCAxNTIuMjUyIDE3Ni45NjUgMTUyLjI1MkMxNzYuNzIzIDE1Mi4yNTIgMTc2LjUzOSAxNTIuMjg0IDE3Ni40MTQgMTUyLjM0OUMxNzYuMjkyIDE1Mi40MTQgMTc2LjE2NiAxNTIuNTAzIDE3Ni4wMzYgMTUyLjYxNVpNMTgyLjcxMyAxNDkuNjM1VjE1NS4zMjJIMTgxLjk1OVYxNDkuNjM1SDE4Mi43MTNaTTE4NS4wOTUgMTUyLjE5M1YxNTIuODFIMTgyLjU0OFYxNTIuMTkzSDE4NS4wOTVaTTE4NS40ODIgMTQ5LjYzNVYxNTAuMjUySDE4Mi41NDhWMTQ5LjYzNUgxODUuNDgyWk0xODguMDIyIDE1NS40QzE4Ny43MjcgMTU1LjQgMTg3LjQ2IDE1NS4zNTEgMTg3LjIyMSAxNTUuMjUyQzE4Ni45ODQgMTU1LjE1IDE4Ni43OCAxNTUuMDA4IDE4Ni42MDggMTU0LjgyNkMxODYuNDM4IDE1NC42NDQgMTg2LjMwOCAxNTQuNDI3IDE4Ni4yMTcgMTU0LjE3N0MxODYuMTI2IDE1My45MjcgMTg2LjA4IDE1My42NTQgMTg2LjA4IDE1My4zNTdWMTUzLjE5M0MxODYuMDggMTUyLjg0OSAxODYuMTMxIDE1Mi41NDMgMTg2LjIzMyAxNTIuMjc1QzE4Ni4zMzQgMTUyLjAwNCAxODYuNDcyIDE1MS43NzUgMTg2LjY0NyAxNTEuNTg4QzE4Ni44MjEgMTUxLjQgMTg3LjAxOSAxNTEuMjU4IDE4Ny4yNCAxNTEuMTYyQzE4Ny40NjIgMTUxLjA2NiAxODcuNjkxIDE1MS4wMTcgMTg3LjkyOCAxNTEuMDE3QzE4OC4yMyAxNTEuMDE3IDE4OC40OSAxNTEuMDY5IDE4OC43MDkgMTUxLjE3NEMxODguOTMxIDE1MS4yNzggMTg5LjExMiAxNTEuNDI0IDE4OS4yNTIgMTUxLjYxMUMxODkuMzkzIDE1MS43OTYgMTg5LjQ5NyAxNTIuMDE1IDE4OS41NjUgMTUyLjI2N0MxODkuNjMyIDE1Mi41MTcgMTg5LjY2NiAxNTIuNzkxIDE4OS42NjYgMTUzLjA4OFYxNTMuNDEySDE4Ni41MVYxNTIuODIySDE4OC45NDRWMTUyLjc2N0MxODguOTMzIDE1Mi41OCAxODguODk0IDE1Mi4zOTggMTg4LjgyNiAxNTIuMjJDMTg4Ljc2MSAxNTIuMDQzIDE4OC42NTcgMTUxLjg5OCAxODguNTE0IDE1MS43ODNDMTg4LjM3MSAxNTEuNjY4IDE4OC4xNzUgMTUxLjYxMSAxODcuOTI4IDE1MS42MTFDMTg3Ljc2NCAxNTEuNjExIDE4Ny42MTMgMTUxLjY0NiAxODcuNDc1IDE1MS43MTdDMTg3LjMzNyAxNTEuNzg0IDE4Ny4yMTggMTUxLjg4NiAxODcuMTE5IDE1Mi4wMjFDMTg3LjAyIDE1Mi4xNTcgMTg2Ljk0NCAxNTIuMzIyIDE4Ni44ODkgMTUyLjUxN0MxODYuODM0IDE1Mi43MTMgMTg2LjgwNyAxNTIuOTM4IDE4Ni44MDcgMTUzLjE5M1YxNTMuMzU3QzE4Ni44MDcgMTUzLjU1OCAxODYuODM0IDE1My43NDcgMTg2Ljg4OSAxNTMuOTI0QzE4Ni45NDYgMTU0LjA5OCAxODcuMDI4IDE1NC4yNTIgMTg3LjEzNSAxNTQuMzg1QzE4Ny4yNDQgMTU0LjUxNyAxODcuMzc2IDE1NC42MjIgMTg3LjUzIDE1NC42OTdDMTg3LjY4NiAxNTQuNzczIDE4Ny44NjMgMTU0LjgxIDE4OC4wNjEgMTU0LjgxQzE4OC4zMTYgMTU0LjgxIDE4OC41MzIgMTU0Ljc1OCAxODguNzA5IDE1NC42NTRDMTg4Ljg4NiAxNTQuNTUgMTg5LjA0MSAxNTQuNDExIDE4OS4xNzQgMTU0LjIzNkwxODkuNjEyIDE1NC41ODRDMTg5LjUyIDE1NC43MjIgMTg5LjQwNSAxNTQuODUzIDE4OS4yNjQgMTU0Ljk3OEMxODkuMTIzIDE1NS4xMDMgMTg4Ljk1IDE1NS4yMDUgMTg4Ljc0NCAxNTUuMjgzQzE4OC41NDEgMTU1LjM2MSAxODguMyAxNTUuNCAxODguMDIyIDE1NS40Wk0xOTAuNTg5IDE0OS4zMjJIMTkxLjMxNVYxNTQuNTAyTDE5MS4yNTMgMTU1LjMyMkgxOTAuNTg5VjE0OS4zMjJaTTE5NC4xNzEgMTUzLjE3NFYxNTMuMjU2QzE5NC4xNzEgMTUzLjU2MyAxOTQuMTM0IDE1My44NDggMTk0LjA2MSAxNTQuMTExQzE5My45ODggMTU0LjM3MiAxOTMuODgyIDE1NC41OTggMTkzLjc0MSAxNTQuNzkxQzE5My42IDE1NC45ODMgMTkzLjQyOSAxNTUuMTMzIDE5My4yMjUgMTU1LjI0QzE5My4wMjIgMTU1LjM0NyAxOTIuNzg5IDE1NS40IDE5Mi41MjYgMTU1LjRDMTkyLjI1OCAxNTUuNCAxOTIuMDIyIDE1NS4zNTUgMTkxLjgxOSAxNTUuMjYzQzE5MS42MTkgMTU1LjE3IDE5MS40NDkgMTU1LjAzNiAxOTEuMzExIDE1NC44NjFDMTkxLjE3MyAxNTQuNjg3IDE5MS4wNjMgMTU0LjQ3NiAxOTAuOTc5IDE1NC4yMjhDMTkwLjg5OSAxNTMuOTgxIDE5MC44NDMgMTUzLjcwMiAxOTAuODExIDE1My4zOTJWMTUzLjAzM0MxOTAuODQzIDE1Mi43MiAxOTAuODk5IDE1Mi40NDEgMTkwLjk3OSAxNTIuMTkzQzE5MS4wNjMgMTUxLjk0NiAxOTEuMTczIDE1MS43MzUgMTkxLjMxMSAxNTEuNTZDMTkxLjQ0OSAxNTEuMzgzIDE5MS42MTkgMTUxLjI0OSAxOTEuODE5IDE1MS4xNThDMTkyLjAyIDE1MS4wNjQgMTkyLjI1MyAxNTEuMDE3IDE5Mi41MTggMTUxLjAxN0MxOTIuNzg0IDE1MS4wMTcgMTkzLjAyIDE1MS4wNjkgMTkzLjIyNSAxNTEuMTc0QzE5My40MzEgMTUxLjI3NSAxOTMuNjAzIDE1MS40MjEgMTkzLjc0MSAxNTEuNjExQzE5My44ODIgMTUxLjgwMSAxOTMuOTg4IDE1Mi4wMjkgMTk0LjA2MSAxNTIuMjk1QzE5NC4xMzQgMTUyLjU1OCAxOTQuMTcxIDE1Mi44NTEgMTk0LjE3MSAxNTMuMTc0Wk0xOTMuNDQ0IDE1My4yNTZWMTUzLjE3NEMxOTMuNDQ0IDE1Mi45NjMgMTkzLjQyNSAxNTIuNzY1IDE5My4zODYgMTUyLjU4QzE5My4zNDcgMTUyLjM5MiAxOTMuMjg0IDE1Mi4yMjggMTkzLjE5OCAxNTIuMDg4QzE5My4xMTIgMTUxLjk0NCAxOTIuOTk5IDE1MS44MzIgMTkyLjg1OCAxNTEuNzUyQzE5Mi43MTggMTUxLjY2OCAxOTIuNTQ0IDE1MS42MjcgMTkyLjMzOSAxNTEuNjI3QzE5Mi4xNTYgMTUxLjYyNyAxOTEuOTk4IDE1MS42NTggMTkxLjg2MiAxNTEuNzJDMTkxLjcyOSAxNTEuNzgzIDE5MS42MTYgMTUxLjg2OCAxOTEuNTIyIDE1MS45NzRDMTkxLjQyOSAxNTIuMDc5IDE5MS4zNTIgMTUyLjE5OCAxOTEuMjkyIDE1Mi4zMzRDMTkxLjIzNSAxNTIuNDY3IDE5MS4xOTIgMTUyLjYwNSAxOTEuMTYzIDE1Mi43NDhWMTUzLjY4OUMxOTEuMjA1IDE1My44NzIgMTkxLjI3MiAxNTQuMDQ3IDE5MS4zNjYgMTU0LjIxN0MxOTEuNDYyIDE1NC4zODMgMTkxLjU5IDE1NC41MiAxOTEuNzQ5IDE1NC42MjdDMTkxLjkxIDE1NC43MzMgMTkyLjExIDE1NC43ODcgMTkyLjM0NyAxNTQuNzg3QzE5Mi41NDIgMTU0Ljc4NyAxOTIuNzA4IDE1NC43NDggMTkyLjg0NyAxNTQuNjdDMTkyLjk4NyAxNTQuNTg5IDE5My4xIDE1NC40NzggMTkzLjE4NiAxNTQuMzM4QzE5My4yNzUgMTU0LjE5NyAxOTMuMzQgMTU0LjAzNCAxOTMuMzgyIDE1My44NDlDMTkzLjQyMyAxNTMuNjY0IDE5My40NDQgMTUzLjQ2NyAxOTMuNDQ0IDE1My4yNTZaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjwvZz4KPHBhdGggZD0iTTI3IDEwNy40NDVMNDAgODguMDk2Mkw3NS45NDA3IDQ0Ljk4OTVDNzYuMjYxMSA0NC42MDUyIDc2LjgxNjEgNDQuNTE2OCA3Ny4yNCA0NC43ODI2TDExMC41NTYgNjUuNjcxNkMxMTAuODM0IDY1Ljg0NiAxMTEuMTggNjUuODcyOCAxMTEuNDgyIDY1Ljc0MzNMMTQ3IDUwLjQ5OUwxODQuMDMyIDM5LjgxODNDMTg0LjMyNyAzOS43MzMxIDE4NC41NjcgMzkuNTE2OCAxODQuNjgyIDM5LjIzMThMMTk4LjUgNSIgc3Ryb2tlPSIjRkZDMTA3IiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cGF0aCBkPSJNMjggMTI5LjE2MUw0MCAxMTIuMTRMNzYgNzkuMTYxNEwxMTEgODkuMjY3OEwxNDcgNzkuMTYxNEwxNzggMTIwLjY1MUwxOTkgODkuMjY3OCIgc3Ryb2tlPSIjNENBRjUwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cGF0aCBkPSJNMjcgMTE4TDQwIDcxLjc3OTJMNzYgNjQuNTYzNUwxMTAuNSAxNy42NjExTDE0Ni41IDQyLjkxNjJMMTgyIDcxLjc3OTJMMTk4LjUgMTA4IiBzdHJva2U9IiMyMTk2RjMiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjwvZz4KPGRlZnM+CjxjbGlwUGF0aCBpZD0iY2xpcDBfNDE4NF85NDUyNyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8Y2xpcFBhdGggaWQ9ImNsaXAxXzQxODRfOTQ1MjciPgo8cmVjdCB3aWR0aD0iMzUuNCIgaGVpZ2h0PSIxMC4zMjIiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1OC4zOTk5IDE0NikiLz4KPC9jbGlwUGF0aD4KPGNsaXBQYXRoIGlkPSJjbGlwMl80MTg0Xzk0NTI3Ij4KPHJlY3Qgd2lkdGg9IjM1LjQiIGhlaWdodD0iMTAuMzIyIiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOTMuOCAxNDYpIi8+CjwvY2xpcFBhdGg+CjxjbGlwUGF0aCBpZD0iY2xpcDNfNDE4NF85NDUyNyI+CjxyZWN0IHdpZHRoPSIzNS40IiBoZWlnaHQ9IjEwLjMyMiIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDEyOS4yIDE0NikiLz4KPC9jbGlwUGF0aD4KPGNsaXBQYXRoIGlkPSJjbGlwNF80MTg0Xzk0NTI3Ij4KPHJlY3Qgd2lkdGg9IjM1LjQiIGhlaWdodD0iMTAuMzIyIiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTY0LjYgMTQ2KSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo=", + "description": "Displays changes to time-series data over time—for example, temperature or humidity readings.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "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 chartType: 'line',\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n dataKeySettingsFunction: TbTimeSeriesChart.dataKeySettings('line'),\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries', units: '°C', decimals: 0 }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-time-series-chart-widget-settings", + "dataKeySettingsDirective": "tb-time-series-chart-key-settings", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-time-series-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":true,\"step\":false,\"stepType\":\"start\",\"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)\",\"pointShape\":\"emptyCircle\",\"pointSize\":4,\"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)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}}},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#FFC107\",\"settings\":{\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":true,\"step\":false,\"stepType\":\"start\",\"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)\",\"pointShape\":\"emptyCircle\",\"pointSize\":4,\"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)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}}},\"_hash\":0.3409583261715494,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"decimals\":null,\"aggregationType\":null,\"usePostProcessing\":null,\"postFuncBody\":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\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"top\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"yAxis\":{\"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)\",\"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)\"},\"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)\",\"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)\"},\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm:ss\",\"lastUpdateAgo\":false,\"custom\":false},\"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,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Line 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\"}" + }, + "tags": [ + "chart", + "time series", + "time-series", + "line", + "line chart" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/persistent_rpc_table.json b/application/src/main/data/json/system/widget_types/persistent_rpc_table.json index 2dadf40b85..f50d1ac5e8 100644 --- a/application/src/main/data/json/system/widget_types/persistent_rpc_table.json +++ b/application/src/main/data/json/system/widget_types/persistent_rpc_table.json @@ -15,7 +15,7 @@ "settingsSchema": "", "dataKeySettingsSchema": "{}\n", "settingsDirective": "tb-persistent-table-widget-settings", - "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"enableStickyAction\":true,\"enableFilter\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"enableStickyHeader\":true,\"displayColumns\":[\"rpcId\",\"messageType\",\"status\",\"method\",\"createdTime\",\"expirationTime\"],\"displayDetails\":true,\"defaultSortOrder\":\"-createdTime\",\"allowSendRequest\":true,\"allowDelete\":true},\"title\":\"Persistent RPC table\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px\"},\"targetDeviceAliasIds\":[]}" + "defaultConfig": "{\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"enableStickyAction\":true,\"enableFilter\":true,\"displayPagination\":true,\"defaultPageSize\":10,\"enableStickyHeader\":true,\"displayColumns\":[\"rpcId\",\"messageType\",\"status\",\"method\",\"createdTime\",\"expirationTime\"],\"displayDetails\":true,\"defaultSortOrder\":\"-createdTime\",\"allowSendRequest\":true,\"allowDelete\":true},\"title\":\"Persistent RPC table\",\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400,\"padding\":\"5px 10px\"}}" }, "externalId": null, "tags": [ diff --git a/application/src/main/data/json/system/widget_types/point_chart.json b/application/src/main/data/json/system/widget_types/point_chart.json new file mode 100644 index 0000000000..f16d6823ee --- /dev/null +++ b/application/src/main/data/json/system/widget_types/point_chart.json @@ -0,0 +1,32 @@ +{ + "fqn": "point_chart", + "name": "Point chart", + "deprecated": false, + "image": "tb-image:Y2hhcnRfKDMpLnN2Zw==:IlBvaW50IGNoYXJ0IiBzeXN0ZW0gd2lkZ2V0IGltYWdl;data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjE2MCIgdmlld0JveD0iMCAwIDIwMCAxNjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMF80MTgyXzExMTkzKSI+CjxwYXRoIGQ9Ik0yLjg0NzY2IDEuMjgxMjVWN0gyLjEyNVYyLjE4MzU5TDAuNjY3OTY5IDIuNzE0ODRWMi4wNjI1TDIuNzM0MzggMS4yODEyNUgyLjg0NzY2Wk04LjY3NTE3IDMuNzAzMTJWNC41NzAzMUM4LjY3NTE3IDUuMDM2NDYgOC42MzM1MSA1LjQyOTY5IDguNTUwMTcgNS43NUM4LjQ2Njg0IDYuMDcwMzEgOC4zNDcwNSA2LjMyODEyIDguMTkwOCA2LjUyMzQ0QzguMDM0NTUgNi43MTg3NSA3Ljg0NTc1IDYuODYwNjggNy42MjQzOSA2Ljk0OTIyQzcuNDA1NjQgNy4wMzUxNiA3LjE1ODI1IDcuMDc4MTIgNi44ODIyIDcuMDc4MTJDNi42NjM0NSA3LjA3ODEyIDYuNDYxNjMgNy4wNTA3OCA2LjI3NjczIDYuOTk2MDlDNi4wOTE4NCA2Ljk0MTQxIDUuOTI1MTcgNi44NTQxNyA1Ljc3NjczIDYuNzM0MzhDNS42MzA5IDYuNjExOTggNS41MDU5IDYuNDUzMTIgNS40MDE3MyA2LjI1NzgxQzUuMjk3NTcgNi4wNjI1IDUuMjE4MTQgNS44MjU1MiA1LjE2MzQ1IDUuNTQ2ODhDNS4xMDg3NyA1LjI2ODIzIDUuMDgxNDIgNC45NDI3MSA1LjA4MTQyIDQuNTcwMzFWMy43MDMxMkM1LjA4MTQyIDMuMjM2OTggNS4xMjMwOSAyLjg0NjM1IDUuMjA2NDIgMi41MzEyNUM1LjI5MjM2IDIuMjE2MTUgNS40MTM0NSAxLjk2MzU0IDUuNTY5NyAxLjc3MzQ0QzUuNzI1OTUgMS41ODA3MyA1LjkxMzQ1IDEuNDQyNzEgNi4xMzIyIDEuMzU5MzhDNi4zNTM1NiAxLjI3NjA0IDYuNjAwOTUgMS4yMzQzOCA2Ljg3NDM5IDEuMjM0MzhDNy4wOTU3NSAxLjIzNDM4IDcuMjk4ODcgMS4yNjE3MiA3LjQ4Mzc3IDEuMzE2NDFDNy42NzEyNyAxLjM2ODQ5IDcuODM3OTMgMS40NTMxMiA3Ljk4Mzc3IDEuNTcwMzFDOC4xMjk2IDEuNjg0OSA4LjI1MzMgMS44Mzg1NCA4LjM1NDg2IDIuMDMxMjVDOC40NTkwMyAyLjIyMTM1IDguNTM4NDUgMi40NTQ0MyA4LjU5MzE0IDIuNzMwNDdDOC42NDc4MyAzLjAwNjUxIDguNjc1MTcgMy4zMzA3MyA4LjY3NTE3IDMuNzAzMTJaTTcuOTQ4NjEgNC42ODc1VjMuNTgyMDNDNy45NDg2MSAzLjMyNjgyIDcuOTMyOTggMy4xMDI4NiA3LjkwMTczIDIuOTEwMTZDNy44NzMwOSAyLjcxNDg0IDcuODMwMTIgMi41NDgxOCA3Ljc3MjgzIDIuNDEwMTZDNy43MTU1NCAyLjI3MjE0IDcuNjQyNjIgMi4xNjAxNiA3LjU1NDA4IDIuMDc0MjJDNy40NjgxNCAxLjk4ODI4IDcuMzY3ODggMS45MjU3OCA3LjI1MzMgMS44ODY3MkM3LjE0MTMyIDEuODQ1MDUgNy4wMTUwMiAxLjgyNDIyIDYuODc0MzkgMS44MjQyMkM2LjcwMjUyIDEuODI0MjIgNi41NTAxNyAxLjg1Njc3IDYuNDE3MzYgMS45MjE4OEM2LjI4NDU1IDEuOTg0MzggNi4xNzI1NyAyLjA4NDY0IDYuMDgxNDIgMi4yMjI2NkM1Ljk5Mjg4IDIuMzYwNjggNS45MjUxNyAyLjU0MTY3IDUuODc4MyAyLjc2NTYyQzUuODMxNDIgMi45ODk1OCA1LjgwNzk4IDMuMjYxNzIgNS44MDc5OCAzLjU4MjAzVjQuNjg3NUM1LjgwNzk4IDQuOTQyNzEgNS44MjIzMSA1LjE2Nzk3IDUuODUwOTUgNS4zNjMyOEM1Ljg4MjIgNS41NTg1OSA1LjkyNzc4IDUuNzI3ODYgNS45ODc2NyA1Ljg3MTA5QzYuMDQ3NTcgNi4wMTE3MiA2LjEyMDQ4IDYuMTI3NiA2LjIwNjQyIDYuMjE4NzVDNi4yOTIzNiA2LjMwOTkgNi4zOTEzMiA2LjM3NzYgNi41MDMzIDYuNDIxODhDNi42MTc4OCA2LjQ2MzU0IDYuNzQ0MTggNi40ODQzOCA2Ljg4MjIgNi40ODQzOEM3LjA1OTI5IDYuNDg0MzggNy4yMTQyMyA2LjQ1MDUyIDcuMzQ3MDUgNi4zODI4MUM3LjQ3OTg2IDYuMzE1MSA3LjU5MDU0IDYuMjA5NjQgNy42NzkwOCA2LjA2NjQxQzcuNzcwMjIgNS45MjA1NyA3LjgzNzkzIDUuNzM0MzggNy44ODIyIDUuNTA3ODFDNy45MjY0NyA1LjI3ODY1IDcuOTQ4NjEgNS4wMDUyMSA3Ljk0ODYxIDQuNjg3NVpNMTMuMzA3NCAzLjcwMzEyVjQuNTcwMzFDMTMuMzA3NCA1LjAzNjQ2IDEzLjI2NTcgNS40Mjk2OSAxMy4xODI0IDUuNzVDMTMuMDk5IDYuMDcwMzEgMTIuOTc5MyA2LjMyODEyIDEyLjgyMyA2LjUyMzQ0QzEyLjY2NjggNi43MTg3NSAxMi40Nzc5IDYuODYwNjggMTIuMjU2NiA2Ljk0OTIyQzEyLjAzNzggNy4wMzUxNiAxMS43OTA0IDcuMDc4MTIgMTEuNTE0NCA3LjA3ODEyQzExLjI5NTcgNy4wNzgxMiAxMS4wOTM4IDcuMDUwNzggMTAuOTA4OSA2Ljk5NjA5QzEwLjcyNCA2Ljk0MTQxIDEwLjU1NzQgNi44NTQxNyAxMC40MDg5IDYuNzM0MzhDMTAuMjYzMSA2LjYxMTk4IDEwLjEzODEgNi40NTMxMiAxMC4wMzM5IDYuMjU3ODFDOS45Mjk3NyA2LjA2MjUgOS44NTAzNCA1LjgyNTUyIDkuNzk1NjYgNS41NDY4OEM5Ljc0MDk3IDUuMjY4MjMgOS43MTM2MyA0Ljk0MjcxIDkuNzEzNjMgNC41NzAzMVYzLjcwMzEyQzkuNzEzNjMgMy4yMzY5OCA5Ljc1NTI5IDIuODQ2MzUgOS44Mzg2MyAyLjUzMTI1QzkuOTI0NTYgMi4yMTYxNSAxMC4wNDU3IDEuOTYzNTQgMTAuMjAxOSAxLjc3MzQ0QzEwLjM1ODIgMS41ODA3MyAxMC41NDU3IDEuNDQyNzEgMTAuNzY0NCAxLjM1OTM4QzEwLjk4NTggMS4yNzYwNCAxMS4yMzMyIDEuMjM0MzggMTEuNTA2NiAxLjIzNDM4QzExLjcyNzkgMS4yMzQzOCAxMS45MzExIDEuMjYxNzIgMTIuMTE2IDEuMzE2NDFDMTIuMzAzNSAxLjM2ODQ5IDEyLjQ3MDEgMS40NTMxMiAxMi42MTYgMS41NzAzMUMxMi43NjE4IDEuNjg0OSAxMi44ODU1IDEuODM4NTQgMTIuOTg3MSAyLjAzMTI1QzEzLjA5MTIgMi4yMjEzNSAxMy4xNzA3IDIuNDU0NDMgMTMuMjI1MyAyLjczMDQ3QzEzLjI4IDMuMDA2NTEgMTMuMzA3NCAzLjMzMDczIDEzLjMwNzQgMy43MDMxMlpNMTIuNTgwOCA0LjY4NzVWMy41ODIwM0MxMi41ODA4IDMuMzI2ODIgMTIuNTY1MiAzLjEwMjg2IDEyLjUzMzkgMi45MTAxNkMxMi41MDUzIDIuNzE0ODQgMTIuNDYyMyAyLjU0ODE4IDEyLjQwNSAyLjQxMDE2QzEyLjM0NzcgMi4yNzIxNCAxMi4yNzQ4IDIuMTYwMTYgMTIuMTg2MyAyLjA3NDIyQzEyLjEwMDMgMS45ODgyOCAxMi4wMDAxIDEuOTI1NzggMTEuODg1NSAxLjg4NjcyQzExLjc3MzUgMS44NDUwNSAxMS42NDcyIDEuODI0MjIgMTEuNTA2NiAxLjgyNDIyQzExLjMzNDcgMS44MjQyMiAxMS4xODI0IDEuODU2NzcgMTEuMDQ5NiAxLjkyMTg4QzEwLjkxNjggMS45ODQzOCAxMC44MDQ4IDIuMDg0NjQgMTAuNzEzNiAyLjIyMjY2QzEwLjYyNTEgMi4zNjA2OCAxMC41NTc0IDIuNTQxNjcgMTAuNTEwNSAyLjc2NTYyQzEwLjQ2MzYgMi45ODk1OCAxMC40NDAyIDMuMjYxNzIgMTAuNDQwMiAzLjU4MjAzVjQuNjg3NUMxMC40NDAyIDQuOTQyNzEgMTAuNDU0NSA1LjE2Nzk3IDEwLjQ4MzIgNS4zNjMyOEMxMC41MTQ0IDUuNTU4NTkgMTAuNTYgNS43Mjc4NiAxMC42MTk5IDUuODcxMDlDMTAuNjc5OCA2LjAxMTcyIDEwLjc1MjcgNi4xMjc2IDEwLjgzODYgNi4yMTg3NUMxMC45MjQ2IDYuMzA5OSAxMS4wMjM1IDYuMzc3NiAxMS4xMzU1IDYuNDIxODhDMTEuMjUwMSA2LjQ2MzU0IDExLjM3NjQgNi40ODQzOCAxMS41MTQ0IDYuNDg0MzhDMTEuNjkxNSA2LjQ4NDM4IDExLjg0NjQgNi40NTA1MiAxMS45NzkzIDYuMzgyODFDMTIuMTEyMSA2LjMxNTEgMTIuMjIyNyA2LjIwOTY0IDEyLjMxMTMgNi4wNjY0MUMxMi40MDI0IDUuOTIwNTcgMTIuNDcwMSA1LjczNDM4IDEyLjUxNDQgNS41MDc4MUMxMi41NTg3IDUuMjc4NjUgMTIuNTgwOCA1LjAwNTIxIDEyLjU4MDggNC42ODc1Wk0xNC4zMDY4IDIuNzA3MDNWMi40MDYyNUMxNC4zMDY4IDIuMTkwMSAxNC4zNTM2IDEuOTkzNDkgMTQuNDQ3NCAxLjgxNjQxQzE0LjU0MTEgMS42MzkzMiAxNC42NzUzIDEuNDk3NCAxNC44NDk3IDEuMzkwNjJDMTUuMDI0MiAxLjI4Mzg1IDE1LjIzMTIgMS4yMzA0NyAxNS40NzA4IDEuMjMwNDdDMTUuNzE1NiAxLjIzMDQ3IDE1LjkyNCAxLjI4Mzg1IDE2LjA5NTggMS4zOTA2MkMxNi4yNzAzIDEuNDk3NCAxNi40MDQ0IDEuNjM5MzIgMTYuNDk4MiAxLjgxNjQxQzE2LjU5MTkgMS45OTM0OSAxNi42Mzg4IDIuMTkwMSAxNi42Mzg4IDIuNDA2MjVWMi43MDcwM0MxNi42Mzg4IDIuOTE3OTcgMTYuNTkxOSAzLjExMTk4IDE2LjQ5ODIgMy4yODkwNkMxNi40MDcgMy40NjYxNSAxNi4yNzQyIDMuNjA4MDcgMTYuMDk5NyAzLjcxNDg0QzE1LjkyNzkgMy44MjE2MSAxNS43MjA4IDMuODc1IDE1LjQ3ODYgMy44NzVDMTUuMjM2NSAzLjg3NSAxNS4wMjY4IDMuODIxNjEgMTQuODQ5NyAzLjcxNDg0QzE0LjY3NTMgMy42MDgwNyAxNC41NDExIDMuNDY2MTUgMTQuNDQ3NCAzLjI4OTA2QzE0LjM1MzYgMy4xMTE5OCAxNC4zMDY4IDIuOTE3OTcgMTQuMzA2OCAyLjcwNzAzWk0xNC44NDk3IDIuNDA2MjVWMi43MDcwM0MxNC44NDk3IDIuODI2ODIgMTQuODcxOSAyLjk0MDEgMTQuOTE2MSAzLjA0Njg4QzE0Ljk2MyAzLjE1MzY1IDE1LjAzMzMgMy4yNDA4OSAxNS4xMjcxIDMuMzA4NTlDMTUuMjIwOCAzLjM3MzcgMTUuMzM4IDMuNDA2MjUgMTUuNDc4NiAzLjQwNjI1QzE1LjYxOTMgMy40MDYyNSAxNS43MzUyIDMuMzczNyAxNS44MjYzIDMuMzA4NTlDMTUuOTE3NCAzLjI0MDg5IDE1Ljk4NTIgMy4xNTM2NSAxNi4wMjk0IDMuMDQ2ODhDMTYuMDczNyAyLjk0MDEgMTYuMDk1OCAyLjgyNjgyIDE2LjA5NTggMi43MDcwM1YyLjQwNjI1QzE2LjA5NTggMi4yODM4NSAxNi4wNzI0IDIuMTY5MjcgMTYuMDI1NSAyLjA2MjVDMTUuOTgxMiAxLjk1MzEyIDE1LjkxMjIgMS44NjU4OSAxNS44MTg1IDEuODAwNzhDMTUuNzI3MyAxLjczMzA3IDE1LjYxMTUgMS42OTkyMiAxNS40NzA4IDEuNjk5MjJDMTUuMzMyOCAxLjY5OTIyIDE1LjIxNjkgMS43MzMwNyAxNS4xMjMyIDEuODAwNzhDMTUuMDMyIDEuODY1ODkgMTQuOTYzIDEuOTUzMTIgMTQuOTE2MSAyLjA2MjVDMTQuODcxOSAyLjE2OTI3IDE0Ljg0OTcgMi4yODM4NSAxNC44NDk3IDIuNDA2MjVaTTE3LjA3NjMgNS45MTAxNlY1LjYwNTQ3QzE3LjA3NjMgNS4zOTE5MyAxNy4xMjMyIDUuMTk2NjEgMTcuMjE2OSA1LjAxOTUzQzE3LjMxMDcgNC44NDI0NSAxNy40NDQ4IDQuNzAwNTIgMTcuNjE5MyA0LjU5Mzc1QzE3Ljc5MzcgNC40ODY5OCAxOC4wMDA4IDQuNDMzNTkgMTguMjQwNCA0LjQzMzU5QzE4LjQ4NTIgNC40MzM1OSAxOC42OTM1IDQuNDg2OTggMTguODY1NCA0LjU5Mzc1QzE5LjAzOTggNC43MDA1MiAxOS4xNzQgNC44NDI0NSAxOS4yNjc3IDUuMDE5NTNDMTkuMzYxNSA1LjE5NjYxIDE5LjQwODMgNS4zOTE5MyAxOS40MDgzIDUuNjA1NDdWNS45MTAxNkMxOS40MDgzIDYuMTIzNyAxOS4zNjE1IDYuMzE5MDEgMTkuMjY3NyA2LjQ5NjA5QzE5LjE3NjYgNi42NzMxOCAxOS4wNDM3IDYuODE1MSAxOC44NjkzIDYuOTIxODhDMTguNjk3NCA3LjAyODY1IDE4LjQ5MDQgNy4wODIwMyAxOC4yNDgyIDcuMDgyMDNDMTguMDA2IDcuMDgyMDMgMTcuNzk3NyA3LjAyODY1IDE3LjYyMzIgNi45MjE4OEMxNy40NDg3IDYuODE1MSAxNy4zMTMzIDYuNjczMTggMTcuMjE2OSA2LjQ5NjA5QzE3LjEyMzIgNi4zMTkwMSAxNy4wNzYzIDYuMTIzNyAxNy4wNzYzIDUuOTEwMTZaTTE3LjYxOTMgNS42MDU0N1Y1LjkxMDE2QzE3LjYxOTMgNi4wMjk5NSAxNy42NDE0IDYuMTQ0NTMgMTcuNjg1NyA2LjI1MzkxQzE3LjczMjUgNi4zNjA2OCAxNy44MDI5IDYuNDQ3OTIgMTcuODk2NiA2LjUxNTYyQzE3Ljk5MDQgNi41ODA3MyAxOC4xMDc1IDYuNjEzMjggMTguMjQ4MiA2LjYxMzI4QzE4LjM4ODggNi42MTMyOCAxOC41MDQ3IDYuNTgwNzMgMTguNTk1OCA2LjUxNTYyQzE4LjY4OTYgNi40NDc5MiAxOC43NTg2IDYuMzYwNjggMTguODAyOSA2LjI1MzkxQzE4Ljg0NzEgNi4xNDcxNCAxOC44NjkzIDYuMDMyNTUgMTguODY5MyA1LjkxMDE2VjUuNjA1NDdDMTguODY5MyA1LjQ4MzA3IDE4Ljg0NTggNS4zNjg0OSAxOC43OTkgNS4yNjE3MkMxOC43NTQ3IDUuMTU0OTUgMTguNjg1NyA1LjA2OTAxIDE4LjU5MTkgNS4wMDM5MUMxOC41MDA4IDQuOTM2MiAxOC4zODM2IDQuOTAyMzQgMTguMjQwNCA0LjkwMjM0QzE4LjEwMjMgNC45MDIzNCAxNy45ODY1IDQuOTM2MiAxNy44OTI3IDUuMDAzOTFDMTcuODAxNiA1LjA2OTAxIDE3LjczMjUgNS4xNTQ5NSAxNy42ODU3IDUuMjYxNzJDMTcuNjQxNCA1LjM2ODQ5IDE3LjYxOTMgNS40ODMwNyAxNy42MTkzIDUuNjA1NDdaTTE4LjQyIDIuMTIxMDlMMTUuNjQyNyA2LjU2NjQxTDE1LjIzNjUgNi4zMDg1OUwxOC4wMTM4IDEuODYzMjhMMTguNDIgMi4xMjEwOVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPHBhdGggZD0iTTguMDU4NTkgMzMuNjY3N0M4LjA1ODU5IDM0LjAxNDEgNy45Nzc4NiAzNC4zMDgzIDcuODE2NDEgMzQuNTUwNUM3LjY1NzU1IDM0Ljc5MDEgNy40NDE0MSAzNC45NzI0IDcuMTY3OTcgMzUuMDk3NEM2Ljg5NzE0IDM1LjIyMjQgNi41OTExNSAzNS4yODQ5IDYuMjUgMzUuMjg0OUM1LjkwODg1IDM1LjI4NDkgNS42MDE1NiAzNS4yMjI0IDUuMzI4MTIgMzUuMDk3NEM1LjA1NDY5IDM0Ljk3MjQgNC44Mzg1NCAzNC43OTAxIDQuNjc5NjkgMzQuNTUwNUM0LjUyMDgzIDM0LjMwODMgNC40NDE0MSAzNC4wMTQxIDQuNDQxNDEgMzMuNjY3N0M0LjQ0MTQxIDMzLjQ0MTIgNC40ODQzOCAzMy4yMzQxIDQuNTcwMzEgMzMuMDQ2NkM0LjY1ODg1IDMyLjg1NjUgNC43ODI1NSAzMi42OTEyIDQuOTQxNDEgMzIuNTUwNUM1LjEwMjg2IDMyLjQwOTkgNS4yOTI5NyAzMi4zMDE4IDUuNTExNzIgMzIuMjI2M0M1LjczMzA3IDMyLjE0ODIgNS45NzY1NiAzMi4xMDkxIDYuMjQyMTkgMzIuMTA5MUM2LjU5MTE1IDMyLjEwOTEgNi45MDIzNCAzMi4xNzY4IDcuMTc1NzggMzIuMzEyM0M3LjQ0OTIyIDMyLjQ0NTEgNy42NjQwNiAzMi42Mjg3IDcuODIwMzEgMzIuODYzQzcuOTc5MTcgMzMuMDk3NCA4LjA1ODU5IDMzLjM2NTYgOC4wNTg1OSAzMy42Njc3Wk03LjMzMjAzIDMzLjY1MjFDNy4zMzIwMyAzMy40NDEyIDcuMjg2NDYgMzMuMjU1IDcuMTk1MzEgMzMuMDkzNUM3LjEwNDE3IDMyLjkyOTQgNi45NzY1NiAzMi44MDE4IDYuODEyNSAzMi43MTA3QzYuNjQ4NDQgMzIuNjE5NSA2LjQ1ODMzIDMyLjU3NCA2LjI0MjE5IDMyLjU3NEM2LjAyMDgzIDMyLjU3NCA1LjgyOTQzIDMyLjYxOTUgNS42Njc5NyAzMi43MTA3QzUuNTA5MTEgMzIuODAxOCA1LjM4NTQyIDMyLjkyOTQgNS4yOTY4OCAzMy4wOTM1QzUuMjA4MzMgMzMuMjU1IDUuMTY0MDYgMzMuNDQxMiA1LjE2NDA2IDMzLjY1MjFDNS4xNjQwNiAzMy44NzA4IDUuMjA3MDMgMzQuMDU4MyA1LjI5Mjk3IDM0LjIxNDZDNS4zODE1MSAzNC4zNjgyIDUuNTA2NTEgMzQuNDg2NyA1LjY2Nzk3IDM0LjU3MDFDNS44MzIwMyAzNC42NTA4IDYuMDI2MDQgMzQuNjkxMiA2LjI1IDM0LjY5MTJDNi40NzM5NiAzNC42OTEyIDYuNjY2NjcgMzQuNjUwOCA2LjgyODEyIDM0LjU3MDFDNi45ODk1OCAzNC40ODY3IDcuMTEzMjggMzQuMzY4MiA3LjE5OTIyIDM0LjIxNDZDNy4yODc3NiAzNC4wNTgzIDcuMzMyMDMgMzMuODcwOCA3LjMzMjAzIDMzLjY1MjFaTTcuOTI1NzggMzAuOTk5OEM3LjkyNTc4IDMxLjI3NTggNy44NTI4NiAzMS41MjQ1IDcuNzA3MDMgMzEuNzQ1OEM3LjU2MTIgMzEuOTY3MiA3LjM2MTk4IDMyLjE0MTcgNy4xMDkzOCAzMi4yNjkzQzYuODU2NzcgMzIuMzk2OSA2LjU3MDMxIDMyLjQ2MDcgNi4yNSAzMi40NjA3QzUuOTI0NDggMzIuNDYwNyA1LjYzNDExIDMyLjM5NjkgNS4zNzg5MSAzMi4yNjkzQzUuMTI2MyAzMi4xNDE3IDQuOTI4MzkgMzEuOTY3MiA0Ljc4NTE2IDMxLjc0NThDNC42NDE5MyAzMS41MjQ1IDQuNTcwMzEgMzEuMjc1OCA0LjU3MDMxIDMwLjk5OThDNC41NzAzMSAzMC42NjkgNC42NDE5MyAzMC4zODc4IDQuNzg1MTYgMzAuMTU2QzQuOTMwOTkgMjkuOTI0MiA1LjEzMDIxIDI5Ljc0NzIgNS4zODI4MSAyOS42MjQ4QzUuNjM1NDIgMjkuNTAyNCA1LjkyMzE4IDI5LjQ0MTIgNi4yNDYwOSAyOS40NDEyQzYuNTcxNjEgMjkuNDQxMiA2Ljg2MDY4IDI5LjUwMjQgNy4xMTMyOCAyOS42MjQ4QzcuMzY1ODkgMjkuNzQ3MiA3LjU2MzggMjkuOTI0MiA3LjcwNzAzIDMwLjE1NkM3Ljg1Mjg2IDMwLjM4NzggNy45MjU3OCAzMC42NjkgNy45MjU3OCAzMC45OTk4Wk03LjIwMzEyIDMxLjAxMTVDNy4yMDMxMiAzMC44MjE0IDcuMTYyNzYgMzAuNjUzNCA3LjA4MjAzIDMwLjUwNzZDNy4wMDEzIDMwLjM2MTcgNi44ODkzMiAzMC4yNDcyIDYuNzQ2MDkgMzAuMTYzOEM2LjYwMjg2IDMwLjA3NzkgNi40MzYyIDMwLjAzNDkgNi4yNDYwOSAzMC4wMzQ5QzYuMDU1OTkgMzAuMDM0OSA1Ljg4OTMyIDMwLjA3NTMgNS43NDYwOSAzMC4xNTZDNS42MDU0NyAzMC4yMzQxIDUuNDk0NzkgMzAuMzQ2MSA1LjQxNDA2IDMwLjQ5MTlDNS4zMzU5NCAzMC42Mzc4IDUuMjk2ODggMzAuODExIDUuMjk2ODggMzEuMDExNUM1LjI5Njg4IDMxLjIwNjggNS4zMzU5NCAzMS4zNzc0IDUuNDE0MDYgMzEuNTIzMkM1LjQ5NDc5IDMxLjY2OSA1LjYwNjc3IDMxLjc4MjMgNS43NSAzMS44NjNDNS44OTMyMyAzMS45NDM4IDYuMDU5OSAzMS45ODQxIDYuMjUgMzEuOTg0MUM2LjQ0MDEgMzEuOTg0MSA2LjYwNTQ3IDMxLjk0MzggNi43NDYwOSAzMS44NjNDNi44ODkzMiAzMS43ODIzIDcuMDAxMyAzMS42NjkgNy4wODIwMyAzMS41MjMyQzcuMTYyNzYgMzEuMzc3NCA3LjIwMzEyIDMxLjIwNjggNy4yMDMxMiAzMS4wMTE1Wk0xMi42NzUyIDMxLjkwOTlWMzIuNzc3MUMxMi42NzUyIDMzLjI0MzIgMTIuNjMzNSAzMy42MzY1IDEyLjU1MDIgMzMuOTU2OEMxMi40NjY4IDM0LjI3NzEgMTIuMzQ3IDM0LjUzNDkgMTIuMTkwOCAzNC43MzAyQzEyLjAzNDUgMzQuOTI1NSAxMS44NDU3IDM1LjA2NzUgMTEuNjI0NCAzNS4xNTZDMTEuNDA1NiAzNS4yNDE5IDExLjE1ODIgMzUuMjg0OSAxMC44ODIyIDM1LjI4NDlDMTAuNjYzNSAzNS4yODQ5IDEwLjQ2MTYgMzUuMjU3NiAxMC4yNzY3IDM1LjIwMjlDMTAuMDkxOCAzNS4xNDgyIDkuOTI1MTcgMzUuMDYxIDkuNzc2NzMgMzQuOTQxMkM5LjYzMDkgMzQuODE4OCA5LjUwNTkgMzQuNjU5OSA5LjQwMTczIDM0LjQ2NDZDOS4yOTc1NyAzNC4yNjkzIDkuMjE4MTQgMzQuMDMyMyA5LjE2MzQ1IDMzLjc1MzdDOS4xMDg3NyAzMy40NzUgOS4wODE0MiAzMy4xNDk1IDkuMDgxNDIgMzIuNzc3MVYzMS45MDk5QzkuMDgxNDIgMzEuNDQzOCA5LjEyMzA5IDMxLjA1MzEgOS4yMDY0MiAzMC43MzhDOS4yOTIzNiAzMC40MjI5IDkuNDEzNDUgMzAuMTcwMyA5LjU2OTcgMjkuOTgwMkM5LjcyNTk1IDI5Ljc4NzUgOS45MTM0NSAyOS42NDk1IDEwLjEzMjIgMjkuNTY2MkMxMC4zNTM2IDI5LjQ4MjggMTAuNjAxIDI5LjQ0MTIgMTAuODc0NCAyOS40NDEyQzExLjA5NTcgMjkuNDQxMiAxMS4yOTg5IDI5LjQ2ODUgMTEuNDgzOCAyOS41MjMyQzExLjY3MTMgMjkuNTc1MyAxMS44Mzc5IDI5LjY1OTkgMTEuOTgzOCAyOS43NzcxQzEyLjEyOTYgMjkuODkxNyAxMi4yNTMzIDMwLjA0NTMgMTIuMzU0OSAzMC4yMzhDMTIuNDU5IDMwLjQyODEgMTIuNTM4NSAzMC42NjEyIDEyLjU5MzEgMzAuOTM3M0MxMi42NDc4IDMxLjIxMzMgMTIuNjc1MiAzMS41Mzc1IDEyLjY3NTIgMzEuOTA5OVpNMTEuOTQ4NiAzMi44OTQzVjMxLjc4ODhDMTEuOTQ4NiAzMS41MzM2IDExLjkzMyAzMS4zMDk3IDExLjkwMTcgMzEuMTE2OUMxMS44NzMxIDMwLjkyMTYgMTEuODMwMSAzMC43NTUgMTEuNzcyOCAzMC42MTY5QzExLjcxNTUgMzAuNDc4OSAxMS42NDI2IDMwLjM2NjkgMTEuNTU0MSAzMC4yODFDMTEuNDY4MSAzMC4xOTUxIDExLjM2NzkgMzAuMTMyNiAxMS4yNTMzIDMwLjA5MzVDMTEuMTQxMyAzMC4wNTE4IDExLjAxNSAzMC4wMzEgMTAuODc0NCAzMC4wMzFDMTAuNzAyNSAzMC4wMzEgMTAuNTUwMiAzMC4wNjM2IDEwLjQxNzQgMzAuMTI4N0MxMC4yODQ1IDMwLjE5MTIgMTAuMTcyNiAzMC4yOTE0IDEwLjA4MTQgMzAuNDI5NEM5Ljk5Mjg4IDMwLjU2NzUgOS45MjUxNyAzMC43NDg1IDkuODc4MyAzMC45NzI0QzkuODMxNDIgMzEuMTk2NCA5LjgwNzk4IDMxLjQ2ODUgOS44MDc5OCAzMS43ODg4VjMyLjg5NDNDOS44MDc5OCAzMy4xNDk1IDkuODIyMzEgMzMuMzc0OCA5Ljg1MDk1IDMzLjU3MDFDOS44ODIyIDMzLjc2NTQgOS45Mjc3OCAzMy45MzQ3IDkuOTg3NjcgMzQuMDc3OUMxMC4wNDc2IDM0LjIxODUgMTAuMTIwNSAzNC4zMzQ0IDEwLjIwNjQgMzQuNDI1NUMxMC4yOTI0IDM0LjUxNjcgMTAuMzkxMyAzNC41ODQ0IDEwLjUwMzMgMzQuNjI4N0MxMC42MTc5IDM0LjY3MDMgMTAuNzQ0MiAzNC42OTEyIDEwLjg4MjIgMzQuNjkxMkMxMS4wNTkzIDM0LjY5MTIgMTEuMjE0MiAzNC42NTczIDExLjM0NyAzNC41ODk2QzExLjQ3OTkgMzQuNTIxOSAxMS41OTA1IDM0LjQxNjQgMTEuNjc5MSAzNC4yNzMyQzExLjc3MDIgMzQuMTI3NCAxMS44Mzc5IDMzLjk0MTIgMTEuODgyMiAzMy43MTQ2QzExLjkyNjUgMzMuNDg1NCAxMS45NDg2IDMzLjIxMiAxMS45NDg2IDMyLjg5NDNaTTEzLjY3NDYgMzAuOTEzOFYzMC42MTNDMTMuNjc0NiAzMC4zOTY5IDEzLjcyMTQgMzAuMjAwMyAxMy44MTUyIDMwLjAyMzJDMTMuOTA4OSAyOS44NDYxIDE0LjA0MzEgMjkuNzA0MiAxNC4yMTc1IDI5LjU5NzRDMTQuMzkyIDI5LjQ5MDYgMTQuNTk5IDI5LjQzNzMgMTQuODM4NiAyOS40MzczQzE1LjA4MzQgMjkuNDM3MyAxNS4yOTE4IDI5LjQ5MDYgMTUuNDYzNiAyOS41OTc0QzE1LjYzODEgMjkuNzA0MiAxNS43NzIyIDI5Ljg0NjEgMTUuODY2IDMwLjAyMzJDMTUuOTU5NyAzMC4yMDAzIDE2LjAwNjYgMzAuMzk2OSAxNi4wMDY2IDMwLjYxM1YzMC45MTM4QzE2LjAwNjYgMzEuMTI0OCAxNS45NTk3IDMxLjMxODggMTUuODY2IDMxLjQ5NThDMTUuNzc0OCAzMS42NzI5IDE1LjY0MiAzMS44MTQ5IDE1LjQ2NzUgMzEuOTIxNkMxNS4yOTU3IDMyLjAyODQgMTUuMDg4NiAzMi4wODE4IDE0Ljg0NjQgMzIuMDgxOEMxNC42MDQzIDMyLjA4MTggMTQuMzk0NiAzMi4wMjg0IDE0LjIxNzUgMzEuOTIxNkMxNC4wNDMxIDMxLjgxNDkgMTMuOTA4OSAzMS42NzI5IDEzLjgxNTIgMzEuNDk1OEMxMy43MjE0IDMxLjMxODggMTMuNjc0NiAzMS4xMjQ4IDEzLjY3NDYgMzAuOTEzOFpNMTQuMjE3NSAzMC42MTNWMzAuOTEzOEMxNC4yMTc1IDMxLjAzMzYgMTQuMjM5NyAzMS4xNDY5IDE0LjI4MzkgMzEuMjUzN0MxNC4zMzA4IDMxLjM2MDQgMTQuNDAxMSAzMS40NDc3IDE0LjQ5NDkgMzEuNTE1NEMxNC41ODg2IDMxLjU4MDUgMTQuNzA1OCAzMS42MTMgMTQuODQ2NCAzMS42MTNDMTQuOTg3MSAzMS42MTMgMTUuMTAyOSAzMS41ODA1IDE1LjE5NDEgMzEuNTE1NEMxNS4yODUyIDMxLjQ0NzcgMTUuMzUyOSAzMS4zNjA0IDE1LjM5NzIgMzEuMjUzN0MxNS40NDE1IDMxLjE0NjkgMTUuNDYzNiAzMS4wMzM2IDE1LjQ2MzYgMzAuOTEzOFYzMC42MTNDMTUuNDYzNiAzMC40OTA2IDE1LjQ0MDIgMzAuMzc2MSAxNS4zOTMzIDMwLjI2OTNDMTUuMzQ5IDMwLjE1OTkgMTUuMjggMzAuMDcyNyAxNS4xODYzIDMwLjAwNzZDMTUuMDk1MSAyOS45Mzk5IDE0Ljk3OTMgMjkuOTA2IDE0LjgzODYgMjkuOTA2QzE0LjcwMDYgMjkuOTA2IDE0LjU4NDcgMjkuOTM5OSAxNC40OTEgMzAuMDA3NkMxNC4zOTk4IDMwLjA3MjcgMTQuMzMwOCAzMC4xNTk5IDE0LjI4MzkgMzAuMjY5M0MxNC4yMzk3IDMwLjM3NjEgMTQuMjE3NSAzMC40OTA2IDE0LjIxNzUgMzAuNjEzWk0xNi40NDQxIDM0LjExNjlWMzMuODEyM0MxNi40NDQxIDMzLjU5ODcgMTYuNDkxIDMzLjQwMzQgMTYuNTg0NyAzMy4yMjYzQzE2LjY3ODUgMzMuMDQ5MiAxNi44MTI2IDMyLjkwNzMgMTYuOTg3MSAzMi44MDA1QzE3LjE2MTUgMzIuNjkzOCAxNy4zNjg2IDMyLjY0MDQgMTcuNjA4MiAzMi42NDA0QzE3Ljg1MjkgMzIuNjQwNCAxOC4wNjEzIDMyLjY5MzggMTguMjMzMiAzMi44MDA1QzE4LjQwNzYgMzIuOTA3MyAxOC41NDE4IDMzLjA0OTIgMTguNjM1NSAzMy4yMjYzQzE4LjcyOTMgMzMuNDAzNCAxOC43NzYxIDMzLjU5ODcgMTguNzc2MSAzMy44MTIzVjM0LjExNjlDMTguNzc2MSAzNC4zMzA1IDE4LjcyOTMgMzQuNTI1OCAxOC42MzU1IDM0LjcwMjlDMTguNTQ0NCAzNC44OCAxOC40MTE1IDM1LjAyMTkgMTguMjM3MSAzNS4xMjg3QzE4LjA2NTIgMzUuMjM1NCAxNy44NTgyIDM1LjI4ODggMTcuNjE2IDM1LjI4ODhDMTcuMzczOCAzNS4yODg4IDE3LjE2NTQgMzUuMjM1NCAxNi45OTEgMzUuMTI4N0MxNi44MTY1IDM1LjAyMTkgMTYuNjgxMSAzNC44OCAxNi41ODQ3IDM0LjcwMjlDMTYuNDkxIDM0LjUyNTggMTYuNDQ0MSAzNC4zMzA1IDE2LjQ0NDEgMzQuMTE2OVpNMTYuOTg3MSAzMy44MTIzVjM0LjExNjlDMTYuOTg3MSAzNC4yMzY3IDE3LjAwOTIgMzQuMzUxMyAxNy4wNTM1IDM0LjQ2MDdDMTcuMTAwMyAzNC41Njc1IDE3LjE3MDcgMzQuNjU0NyAxNy4yNjQ0IDM0LjcyMjRDMTcuMzU4MiAzNC43ODc1IDE3LjQ3NTMgMzQuODIwMSAxNy42MTYgMzQuODIwMUMxNy43NTY2IDM0LjgyMDEgMTcuODcyNSAzNC43ODc1IDE3Ljk2MzYgMzQuNzIyNEMxOC4wNTc0IDM0LjY1NDcgMTguMTI2NCAzNC41Njc1IDE4LjE3MDcgMzQuNDYwN0MxOC4yMTQ5IDM0LjM1MzkgMTguMjM3MSAzNC4yMzkzIDE4LjIzNzEgMzQuMTE2OVYzMy44MTIzQzE4LjIzNzEgMzMuNjg5OSAxOC4yMTM2IDMzLjU3NTMgMTguMTY2OCAzMy40Njg1QzE4LjEyMjUgMzMuMzYxNyAxOC4wNTM1IDMzLjI3NTggMTcuOTU5NyAzMy4yMTA3QzE3Ljg2ODYgMzMuMTQzIDE3Ljc1MTQgMzMuMTA5MSAxNy42MDgyIDMzLjEwOTFDMTcuNDcwMSAzMy4xMDkxIDE3LjM1NDMgMzMuMTQzIDE3LjI2MDUgMzMuMjEwN0MxNy4xNjk0IDMzLjI3NTggMTcuMTAwMyAzMy4zNjE3IDE3LjA1MzUgMzMuNDY4NUMxNy4wMDkyIDMzLjU3NTMgMTYuOTg3MSAzMy42ODk5IDE2Ljk4NzEgMzMuODEyM1pNMTcuNzg3OCAzMC4zMjc5TDE1LjAxMDUgMzQuNzczMkwxNC42MDQzIDM0LjUxNTRMMTcuMzgxNiAzMC4wNzAxTDE3Ljc4NzggMzAuMzI3OVoiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPHBhdGggZD0iTTcuMjQ2MDkgNTcuNzE4M0g3LjMwODU5VjU4LjMzMTVINy4yNDYwOUM2Ljg2MzI4IDU4LjMzMTUgNi41NDI5NyA1OC4zOTQgNi4yODUxNiA1OC41MTlDNi4wMjczNCA1OC42NDE0IDUuODIyOTIgNTguODA2OCA1LjY3MTg4IDU5LjAxNTFDNS41MjA4MyA1OS4yMjA5IDUuNDExNDYgNTkuNDUyNiA1LjM0Mzc1IDU5LjcxMDRDNS4yNzg2NSA1OS45NjgzIDUuMjQ2MDkgNjAuMjMgNS4yNDYwOSA2MC40OTU2VjYxLjMzMTVDNS4yNDYwOSA2MS41ODQxIDUuMjc2MDQgNjEuODA4MSA1LjMzNTk0IDYyLjAwMzRDNS4zOTU4MyA2Mi4xOTYxIDUuNDc3ODYgNjIuMzU4OSA1LjU4MjAzIDYyLjQ5MTdDNS42ODYyIDYyLjYyNDUgNS44MDMzOSA2Mi43MjQ4IDUuOTMzNTkgNjIuNzkyNUM2LjA2NjQxIDYyLjg2MDIgNi4yMDQ0MyA2Mi44OTQgNi4zNDc2NiA2Mi44OTRDNi41MTQzMiA2Mi44OTQgNi42NjI3NiA2Mi44NjI4IDYuNzkyOTcgNjIuODAwM0M2LjkyMzE4IDYyLjczNTIgNy4wMzI1NSA2Mi42NDUzIDcuMTIxMDkgNjIuNTMwOEM3LjIxMjI0IDYyLjQxMzYgNy4yODEyNSA2Mi4yNzU2IDcuMzI4MTIgNjIuMTE2N0M3LjM3NSA2MS45NTc4IDcuMzk4NDQgNjEuNzgzNCA3LjM5ODQ0IDYxLjU5MzNDNy4zOTg0NCA2MS40MjQgNy4zNzc2IDYxLjI2MTIgNy4zMzU5NCA2MS4xMDVDNy4yOTQyNyA2MC45NDYxIDcuMjMwNDcgNjAuODA1NSA3LjE0NDUzIDYwLjY4MzFDNy4wNTg1OSA2MC41NTgxIDYuOTUwNTIgNjAuNDYwNCA2LjgyMDMxIDYwLjM5MDFDNi42OTI3MSA2MC4zMTcyIDYuNTQwMzYgNjAuMjgwOCA2LjM2MzI4IDYwLjI4MDhDNi4xNjI3NiA2MC4yODA4IDUuOTc1MjYgNjAuMzMwMiA1LjgwMDc4IDYwLjQyOTJDNS42Mjg5MSA2MC41MjU2IDUuNDg2OTggNjAuNjUzMiA1LjM3NSA2MC44MTJDNS4yNjU2MiA2MC45NjgzIDUuMjAzMTIgNjEuMTM4OCA1LjE4NzUgNjEuMzIzN0w0LjgwNDY5IDYxLjMxOThDNC44NDExNSA2MS4wMjgyIDQuOTA4ODUgNjAuNzc5NSA1LjAwNzgxIDYwLjU3MzdDNS4xMDkzOCA2MC4zNjU0IDUuMjM0MzggNjAuMTk2MSA1LjM4MjgxIDYwLjA2NTlDNS41MzM4NSA1OS45MzMxIDUuNzAxODIgNTkuODM2OCA1Ljg4NjcyIDU5Ljc3NjlDNi4wNzQyMiA1OS43MTQ0IDYuMjcyMTQgNTkuNjgzMSA2LjQ4MDQ3IDU5LjY4MzFDNi43NjQzMiA1OS42ODMxIDcuMDA5MTEgNTkuNzM2NSA3LjIxNDg0IDU5Ljg0MzNDNy40MjA1NyA1OS45NSA3LjU4OTg0IDYwLjA5MzMgNy43MjI2NiA2MC4yNzI5QzcuODU1NDcgNjAuNDUgNy45NTMxMiA2MC42NTA2IDguMDE1NjIgNjAuODc0NUM4LjA4MDczIDYxLjA5NTkgOC4xMTMyOCA2MS4zMjM3IDguMTEzMjggNjEuNTU4MUM4LjExMzI4IDYxLjgyNjMgOC4wNzU1MiA2Mi4wNzc2IDggNjIuMzEyQzcuOTI0NDggNjIuNTQ2NCA3LjgxMTIgNjIuNzUyMSA3LjY2MDE2IDYyLjkyOTJDNy41MTE3MiA2My4xMDYzIDcuMzI4MTIgNjMuMjQ0MyA3LjEwOTM4IDYzLjM0MzNDNi44OTA2MiA2My40NDIyIDYuNjM2NzIgNjMuNDkxNyA2LjM0NzY2IDYzLjQ5MTdDNi4wNDAzNiA2My40OTE3IDUuNzcyMTQgNjMuNDI5MiA1LjU0Mjk3IDYzLjMwNDJDNS4zMTM4IDYzLjE3NjYgNS4xMjM3IDYzLjAwNzMgNC45NzI2NiA2Mi43OTY0QzQuODIxNjEgNjIuNTg1NCA0LjcwODMzIDYyLjM1MTEgNC42MzI4MSA2Mi4wOTMzQzQuNTU3MjkgNjEuODM1NCA0LjUxOTUzIDYxLjU3MzcgNC41MTk1MyA2MS4zMDgxVjYwLjk2ODNDNC41MTk1MyA2MC41NjcyIDQuNTU5OSA2MC4xNzQgNC42NDA2MiA1OS43ODg2QzQuNzIxMzUgNTkuNDAzMiA0Ljg2MDY4IDU5LjA1NDIgNS4wNTg1OSA1OC43NDE3QzUuMjU5MTEgNTguNDI5MiA1LjUzNjQ2IDU4LjE4MDUgNS44OTA2MiA1Ny45OTU2QzYuMjQ0NzkgNTcuODEwNyA2LjY5NjYxIDU3LjcxODMgNy4yNDYwOSA1Ny43MTgzWk0xMi42NzUyIDYwLjExNjdWNjAuOTgzOUMxMi42NzUyIDYxLjQ1IDEyLjYzMzUgNjEuODQzMyAxMi41NTAyIDYyLjE2MzZDMTIuNDY2OCA2Mi40ODM5IDEyLjM0NyA2Mi43NDE3IDEyLjE5MDggNjIuOTM3QzEyLjAzNDUgNjMuMTMyMyAxMS44NDU3IDYzLjI3NDMgMTEuNjI0NCA2My4zNjI4QzExLjQwNTYgNjMuNDQ4NyAxMS4xNTgyIDYzLjQ5MTcgMTAuODgyMiA2My40OTE3QzEwLjY2MzUgNjMuNDkxNyAxMC40NjE2IDYzLjQ2NDQgMTAuMjc2NyA2My40MDk3QzEwLjA5MTggNjMuMzU1IDkuOTI1MTcgNjMuMjY3NyA5Ljc3NjczIDYzLjE0NzlDOS42MzA5IDYzLjAyNTYgOS41MDU5IDYyLjg2NjcgOS40MDE3MyA2Mi42NzE0QzkuMjk3NTcgNjIuNDc2MSA5LjIxODE0IDYyLjIzOTEgOS4xNjM0NSA2MS45NjA0QzkuMTA4NzcgNjEuNjgxOCA5LjA4MTQyIDYxLjM1NjMgOS4wODE0MiA2MC45ODM5VjYwLjExNjdDOS4wODE0MiA1OS42NTA2IDkuMTIzMDkgNTkuMjU5OSA5LjIwNjQyIDU4Ljk0NDhDOS4yOTIzNiA1OC42Mjk3IDkuNDEzNDUgNTguMzc3MSA5LjU2OTcgNTguMTg3QzkuNzI1OTUgNTcuOTk0MyA5LjkxMzQ1IDU3Ljg1NjMgMTAuMTMyMiA1Ny43NzI5QzEwLjM1MzYgNTcuNjg5NiAxMC42MDEgNTcuNjQ3OSAxMC44NzQ0IDU3LjY0NzlDMTEuMDk1NyA1Ny42NDc5IDExLjI5ODkgNTcuNjc1MyAxMS40ODM4IDU3LjczQzExLjY3MTMgNTcuNzgyMSAxMS44Mzc5IDU3Ljg2NjcgMTEuOTgzOCA1Ny45ODM5QzEyLjEyOTYgNTguMDk4NSAxMi4yNTMzIDU4LjI1MjEgMTIuMzU0OSA1OC40NDQ4QzEyLjQ1OSA1OC42MzQ5IDEyLjUzODUgNTguODY4IDEyLjU5MzEgNTkuMTQ0QzEyLjY0NzggNTkuNDIwMSAxMi42NzUyIDU5Ljc0NDMgMTIuNjc1MiA2MC4xMTY3Wk0xMS45NDg2IDYxLjEwMTFWNTkuOTk1NkMxMS45NDg2IDU5Ljc0MDQgMTEuOTMzIDU5LjUxNjQgMTEuOTAxNyA1OS4zMjM3QzExLjg3MzEgNTkuMTI4NCAxMS44MzAxIDU4Ljk2MTggMTEuNzcyOCA1OC44MjM3QzExLjcxNTUgNTguNjg1NyAxMS42NDI2IDU4LjU3MzcgMTEuNTU0MSA1OC40ODc4QzExLjQ2ODEgNTguNDAxOSAxMS4zNjc5IDU4LjMzOTQgMTEuMjUzMyA1OC4zMDAzQzExLjE0MTMgNTguMjU4NiAxMS4wMTUgNTguMjM3OCAxMC44NzQ0IDU4LjIzNzhDMTAuNzAyNSA1OC4yMzc4IDEwLjU1MDIgNTguMjcwMyAxMC40MTc0IDU4LjMzNTRDMTAuMjg0NSA1OC4zOTc5IDEwLjE3MjYgNTguNDk4MiAxMC4wODE0IDU4LjYzNjJDOS45OTI4OCA1OC43NzQzIDkuOTI1MTcgNTguOTU1MiA5Ljg3ODMgNTkuMTc5MkM5LjgzMTQyIDU5LjQwMzIgOS44MDc5OCA1OS42NzUzIDkuODA3OTggNTkuOTk1NlY2MS4xMDExQzkuODA3OTggNjEuMzU2MyA5LjgyMjMxIDYxLjU4MTUgOS44NTA5NSA2MS43NzY5QzkuODgyMiA2MS45NzIyIDkuOTI3NzggNjIuMTQxNCA5Ljk4NzY3IDYyLjI4NDdDMTAuMDQ3NiA2Mi40MjUzIDEwLjEyMDUgNjIuNTQxMiAxMC4yMDY0IDYyLjYzMjNDMTAuMjkyNCA2Mi43MjM1IDEwLjM5MTMgNjIuNzkxMiAxMC41MDMzIDYyLjgzNTRDMTAuNjE3OSA2Mi44NzcxIDEwLjc0NDIgNjIuODk3OSAxMC44ODIyIDYyLjg5NzlDMTEuMDU5MyA2Mi44OTc5IDExLjIxNDIgNjIuODY0MSAxMS4zNDcgNjIuNzk2NEMxMS40Nzk5IDYyLjcyODcgMTEuNTkwNSA2Mi42MjMyIDExLjY3OTEgNjIuNDhDMTEuNzcwMiA2Mi4zMzQxIDExLjgzNzkgNjIuMTQ3OSAxMS44ODIyIDYxLjkyMTRDMTEuOTI2NSA2MS42OTIyIDExLjk0ODYgNjEuNDE4OCAxMS45NDg2IDYxLjEwMTFaTTEzLjY3NDYgNTkuMTIwNlY1OC44MTk4QzEzLjY3NDYgNTguNjAzNyAxMy43MjE0IDU4LjQwNzEgMTMuODE1MiA1OC4yM0MxMy45MDg5IDU4LjA1MjkgMTQuMDQzMSA1Ny45MTEgMTQuMjE3NSA1Ny44MDQyQzE0LjM5MiA1Ny42OTc0IDE0LjU5OSA1Ny42NDQgMTQuODM4NiA1Ny42NDRDMTUuMDgzNCA1Ny42NDQgMTUuMjkxOCA1Ny42OTc0IDE1LjQ2MzYgNTcuODA0MkMxNS42MzgxIDU3LjkxMSAxNS43NzIyIDU4LjA1MjkgMTUuODY2IDU4LjIzQzE1Ljk1OTcgNTguNDA3MSAxNi4wMDY2IDU4LjYwMzcgMTYuMDA2NiA1OC44MTk4VjU5LjEyMDZDMTYuMDA2NiA1OS4zMzE1IDE1Ljk1OTcgNTkuNTI1NiAxNS44NjYgNTkuNzAyNkMxNS43NzQ4IDU5Ljg3OTcgMTUuNjQyIDYwLjAyMTYgMTUuNDY3NSA2MC4xMjg0QzE1LjI5NTcgNjAuMjM1MiAxNS4wODg2IDYwLjI4ODYgMTQuODQ2NCA2MC4yODg2QzE0LjYwNDMgNjAuMjg4NiAxNC4zOTQ2IDYwLjIzNTIgMTQuMjE3NSA2MC4xMjg0QzE0LjA0MzEgNjAuMDIxNiAxMy45MDg5IDU5Ljg3OTcgMTMuODE1MiA1OS43MDI2QzEzLjcyMTQgNTkuNTI1NiAxMy42NzQ2IDU5LjMzMTUgMTMuNjc0NiA1OS4xMjA2Wk0xNC4yMTc1IDU4LjgxOThWNTkuMTIwNkMxNC4yMTc1IDU5LjI0MDQgMTQuMjM5NyA1OS4zNTM3IDE0LjI4MzkgNTkuNDYwNEMxNC4zMzA4IDU5LjU2NzIgMTQuNDAxMSA1OS42NTQ1IDE0LjQ5NDkgNTkuNzIyMkMxNC41ODg2IDU5Ljc4NzMgMTQuNzA1OCA1OS44MTk4IDE0Ljg0NjQgNTkuODE5OEMxNC45ODcxIDU5LjgxOTggMTUuMTAyOSA1OS43ODczIDE1LjE5NDEgNTkuNzIyMkMxNS4yODUyIDU5LjY1NDUgMTUuMzUyOSA1OS41NjcyIDE1LjM5NzIgNTkuNDYwNEMxNS40NDE1IDU5LjM1MzcgMTUuNDYzNiA1OS4yNDA0IDE1LjQ2MzYgNTkuMTIwNlY1OC44MTk4QzE1LjQ2MzYgNTguNjk3NCAxNS40NDAyIDU4LjU4MjggMTUuMzkzMyA1OC40NzYxQzE1LjM0OSA1OC4zNjY3IDE1LjI4IDU4LjI3OTUgMTUuMTg2MyA1OC4yMTQ0QzE1LjA5NTEgNTguMTQ2NiAxNC45NzkzIDU4LjExMjggMTQuODM4NiA1OC4xMTI4QzE0LjcwMDYgNTguMTEyOCAxNC41ODQ3IDU4LjE0NjYgMTQuNDkxIDU4LjIxNDRDMTQuMzk5OCA1OC4yNzk1IDE0LjMzMDggNTguMzY2NyAxNC4yODM5IDU4LjQ3NjFDMTQuMjM5NyA1OC41ODI4IDE0LjIxNzUgNTguNjk3NCAxNC4yMTc1IDU4LjgxOThaTTE2LjQ0NDEgNjIuMzIzN1Y2Mi4wMTlDMTYuNDQ0MSA2MS44MDU1IDE2LjQ5MSA2MS42MTAyIDE2LjU4NDcgNjEuNDMzMUMxNi42Nzg1IDYxLjI1NiAxNi44MTI2IDYxLjExNDEgMTYuOTg3MSA2MS4wMDczQzE3LjE2MTUgNjAuOTAwNiAxNy4zNjg2IDYwLjg0NzIgMTcuNjA4MiA2MC44NDcyQzE3Ljg1MjkgNjAuODQ3MiAxOC4wNjEzIDYwLjkwMDYgMTguMjMzMiA2MS4wMDczQzE4LjQwNzYgNjEuMTE0MSAxOC41NDE4IDYxLjI1NiAxOC42MzU1IDYxLjQzMzFDMTguNzI5MyA2MS42MTAyIDE4Ljc3NjEgNjEuODA1NSAxOC43NzYxIDYyLjAxOVY2Mi4zMjM3QzE4Ljc3NjEgNjIuNTM3MyAxOC43MjkzIDYyLjczMjYgMTguNjM1NSA2Mi45MDk3QzE4LjU0NDQgNjMuMDg2OCAxOC40MTE1IDYzLjIyODcgMTguMjM3MSA2My4zMzU0QzE4LjA2NTIgNjMuNDQyMiAxNy44NTgyIDYzLjQ5NTYgMTcuNjE2IDYzLjQ5NTZDMTcuMzczOCA2My40OTU2IDE3LjE2NTQgNjMuNDQyMiAxNi45OTEgNjMuMzM1NEMxNi44MTY1IDYzLjIyODcgMTYuNjgxMSA2My4wODY4IDE2LjU4NDcgNjIuOTA5N0MxNi40OTEgNjIuNzMyNiAxNi40NDQxIDYyLjUzNzMgMTYuNDQ0MSA2Mi4zMjM3Wk0xNi45ODcxIDYyLjAxOVY2Mi4zMjM3QzE2Ljk4NzEgNjIuNDQzNSAxNy4wMDkyIDYyLjU1ODEgMTcuMDUzNSA2Mi42Njc1QzE3LjEwMDMgNjIuNzc0MyAxNy4xNzA3IDYyLjg2MTUgMTcuMjY0NCA2Mi45MjkyQzE3LjM1ODIgNjIuOTk0MyAxNy40NzUzIDYzLjAyNjkgMTcuNjE2IDYzLjAyNjlDMTcuNzU2NiA2My4wMjY5IDE3Ljg3MjUgNjIuOTk0MyAxNy45NjM2IDYyLjkyOTJDMTguMDU3NCA2Mi44NjE1IDE4LjEyNjQgNjIuNzc0MyAxOC4xNzA3IDYyLjY2NzVDMTguMjE0OSA2Mi41NjA3IDE4LjIzNzEgNjIuNDQ2MSAxOC4yMzcxIDYyLjMyMzdWNjIuMDE5QzE4LjIzNzEgNjEuODk2NiAxOC4yMTM2IDYxLjc4MjEgMTguMTY2OCA2MS42NzUzQzE4LjEyMjUgNjEuNTY4NSAxOC4wNTM1IDYxLjQ4MjYgMTcuOTU5NyA2MS40MTc1QzE3Ljg2ODYgNjEuMzQ5OCAxNy43NTE0IDYxLjMxNTkgMTcuNjA4MiA2MS4zMTU5QzE3LjQ3MDEgNjEuMzE1OSAxNy4zNTQzIDYxLjM0OTggMTcuMjYwNSA2MS40MTc1QzE3LjE2OTQgNjEuNDgyNiAxNy4xMDAzIDYxLjU2ODUgMTcuMDUzNSA2MS42NzUzQzE3LjAwOTIgNjEuNzgyMSAxNi45ODcxIDYxLjg5NjYgMTYuOTg3MSA2Mi4wMTlaTTE3Ljc4NzggNTguNTM0N0wxNS4wMTA1IDYyLjk4TDE0LjYwNDMgNjIuNzIyMkwxNy4zODE2IDU4LjI3NjlMMTcuNzg3OCA1OC41MzQ3WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNOC4zMTY0MSA4OS43MDYzVjkwLjNINC4yMDcwM1Y4OS44NzQzTDYuNzUzOTEgODUuOTMyOUg3LjM0Mzc1TDYuNzEwOTQgODcuMDczNUw1LjAyNzM0IDg5LjcwNjNIOC4zMTY0MVpNNy41MjM0NCA4NS45MzI5VjkxLjYyMDRINi44MDA3OFY4NS45MzI5SDcuNTIzNDRaTTEyLjY3NTIgODguMzIzNVY4OS4xOTA3QzEyLjY3NTIgODkuNjU2OCAxMi42MzM1IDkwLjA1IDEyLjU1MDIgOTAuMzcwNEMxMi40NjY4IDkwLjY5MDcgMTIuMzQ3IDkwLjk0ODUgMTIuMTkwOCA5MS4xNDM4QzEyLjAzNDUgOTEuMzM5MSAxMS44NDU3IDkxLjQ4MSAxMS42MjQ0IDkxLjU2OTZDMTEuNDA1NiA5MS42NTU1IDExLjE1ODIgOTEuNjk4NSAxMC44ODIyIDkxLjY5ODVDMTAuNjYzNSA5MS42OTg1IDEwLjQ2MTYgOTEuNjcxMSAxMC4yNzY3IDkxLjYxNjVDMTAuMDkxOCA5MS41NjE4IDkuOTI1MTcgOTEuNDc0NSA5Ljc3NjczIDkxLjM1NDdDOS42MzA5IDkxLjIzMjMgOS41MDU5IDkxLjA3MzUgOS40MDE3MyA5MC44NzgyQzkuMjk3NTcgOTAuNjgyOSA5LjIxODE0IDkwLjQ0NTkgOS4xNjM0NSA5MC4xNjcyQzkuMTA4NzcgODkuODg4NiA5LjA4MTQyIDg5LjU2MzEgOS4wODE0MiA4OS4xOTA3Vjg4LjMyMzVDOS4wODE0MiA4Ny44NTczIDkuMTIzMDkgODcuNDY2NyA5LjIwNjQyIDg3LjE1MTZDOS4yOTIzNiA4Ni44MzY1IDkuNDEzNDUgODYuNTgzOSA5LjU2OTcgODYuMzkzOEM5LjcyNTk1IDg2LjIwMTEgOS45MTM0NSA4Ni4wNjMxIDEwLjEzMjIgODUuOTc5N0MxMC4zNTM2IDg1Ljg5NjQgMTAuNjAxIDg1Ljg1NDcgMTAuODc0NCA4NS44NTQ3QzExLjA5NTcgODUuODU0NyAxMS4yOTg5IDg1Ljg4MjEgMTEuNDgzOCA4NS45MzY4QzExLjY3MTMgODUuOTg4OSAxMS44Mzc5IDg2LjA3MzUgMTEuOTgzOCA4Ni4xOTA3QzEyLjEyOTYgODYuMzA1MyAxMi4yNTMzIDg2LjQ1ODkgMTIuMzU0OSA4Ni42NTE2QzEyLjQ1OSA4Ni44NDE3IDEyLjUzODUgODcuMDc0OCAxMi41OTMxIDg3LjM1MDhDMTIuNjQ3OCA4Ny42MjY5IDEyLjY3NTIgODcuOTUxMSAxMi42NzUyIDg4LjMyMzVaTTExLjk0ODYgODkuMzA3OVY4OC4yMDI0QzExLjk0ODYgODcuOTQ3MiAxMS45MzMgODcuNzIzMiAxMS45MDE3IDg3LjUzMDVDMTEuODczMSA4Ny4zMzUyIDExLjgzMDEgODcuMTY4NSAxMS43NzI4IDg3LjAzMDVDMTEuNzE1NSA4Ni44OTI1IDExLjY0MjYgODYuNzgwNSAxMS41NTQxIDg2LjY5NDZDMTEuNDY4MSA4Ni42MDg2IDExLjM2NzkgODYuNTQ2MSAxMS4yNTMzIDg2LjUwNzFDMTEuMTQxMyA4Ni40NjU0IDExLjAxNSA4Ni40NDQ2IDEwLjg3NDQgODYuNDQ0NkMxMC43MDI1IDg2LjQ0NDYgMTAuNTUwMiA4Ni40NzcxIDEwLjQxNzQgODYuNTQyMkMxMC4yODQ1IDg2LjYwNDcgMTAuMTcyNiA4Ni43MDUgMTAuMDgxNCA4Ni44NDNDOS45OTI4OCA4Ni45ODEgOS45MjUxNyA4Ny4xNjIgOS44NzgzIDg3LjM4NkM5LjgzMTQyIDg3LjYwOTkgOS44MDc5OCA4Ny44ODIxIDkuODA3OTggODguMjAyNFY4OS4zMDc5QzkuODA3OTggODkuNTYzMSA5LjgyMjMxIDg5Ljc4ODMgOS44NTA5NSA4OS45ODM2QzkuODgyMiA5MC4xNzkgOS45Mjc3OCA5MC4zNDgyIDkuOTg3NjcgOTAuNDkxNUMxMC4wNDc2IDkwLjYzMjEgMTAuMTIwNSA5MC43NDggMTAuMjA2NCA5MC44MzkxQzEwLjI5MjQgOTAuOTMwMyAxMC4zOTEzIDkwLjk5OCAxMC41MDMzIDkxLjA0MjJDMTAuNjE3OSA5MS4wODM5IDEwLjc0NDIgOTEuMTA0NyAxMC44ODIyIDkxLjEwNDdDMTEuMDU5MyA5MS4xMDQ3IDExLjIxNDIgOTEuMDcwOSAxMS4zNDcgOTEuMDAzMkMxMS40Nzk5IDkwLjkzNTUgMTEuNTkwNSA5MC44MyAxMS42NzkxIDkwLjY4NjhDMTEuNzcwMiA5MC41NDA5IDExLjgzNzkgOTAuMzU0NyAxMS44ODIyIDkwLjEyODJDMTEuOTI2NSA4OS44OTkgMTEuOTQ4NiA4OS42MjU2IDExLjk0ODYgODkuMzA3OVpNMTMuNjc0NiA4Ny4zMjc0Vjg3LjAyNjZDMTMuNjc0NiA4Ni44MTA1IDEzLjcyMTQgODYuNjEzOSAxMy44MTUyIDg2LjQzNjhDMTMuOTA4OSA4Ni4yNTk3IDE0LjA0MzEgODYuMTE3OCAxNC4yMTc1IDg2LjAxMUMxNC4zOTIgODUuOTA0MiAxNC41OTkgODUuODUwOCAxNC44Mzg2IDg1Ljg1MDhDMTUuMDgzNCA4NS44NTA4IDE1LjI5MTggODUuOTA0MiAxNS40NjM2IDg2LjAxMUMxNS42MzgxIDg2LjExNzggMTUuNzcyMiA4Ni4yNTk3IDE1Ljg2NiA4Ni40MzY4QzE1Ljk1OTcgODYuNjEzOSAxNi4wMDY2IDg2LjgxMDUgMTYuMDA2NiA4Ny4wMjY2Vjg3LjMyNzRDMTYuMDA2NiA4Ny41MzgzIDE1Ljk1OTcgODcuNzMyMyAxNS44NjYgODcuOTA5NEMxNS43NzQ4IDg4LjA4NjUgMTUuNjQyIDg4LjIyODQgMTUuNDY3NSA4OC4zMzUyQzE1LjI5NTcgODguNDQyIDE1LjA4ODYgODguNDk1NCAxNC44NDY0IDg4LjQ5NTRDMTQuNjA0MyA4OC40OTU0IDE0LjM5NDYgODguNDQyIDE0LjIxNzUgODguMzM1MkMxNC4wNDMxIDg4LjIyODQgMTMuOTA4OSA4OC4wODY1IDEzLjgxNTIgODcuOTA5NEMxMy43MjE0IDg3LjczMjMgMTMuNjc0NiA4Ny41MzgzIDEzLjY3NDYgODcuMzI3NFpNMTQuMjE3NSA4Ny4wMjY2Vjg3LjMyNzRDMTQuMjE3NSA4Ny40NDcyIDE0LjIzOTcgODcuNTYwNSAxNC4yODM5IDg3LjY2NzJDMTQuMzMwOCA4Ny43NzQgMTQuNDAxMSA4Ny44NjEyIDE0LjQ5NDkgODcuOTI5QzE0LjU4ODYgODcuOTk0MSAxNC43MDU4IDg4LjAyNjYgMTQuODQ2NCA4OC4wMjY2QzE0Ljk4NzEgODguMDI2NiAxNS4xMDI5IDg3Ljk5NDEgMTUuMTk0MSA4Ny45MjlDMTUuMjg1MiA4Ny44NjEyIDE1LjM1MjkgODcuNzc0IDE1LjM5NzIgODcuNjY3MkMxNS40NDE1IDg3LjU2MDUgMTUuNDYzNiA4Ny40NDcyIDE1LjQ2MzYgODcuMzI3NFY4Ny4wMjY2QzE1LjQ2MzYgODYuOTA0MiAxNS40NDAyIDg2Ljc4OTYgMTUuMzkzMyA4Ni42ODI5QzE1LjM0OSA4Ni41NzM1IDE1LjI4IDg2LjQ4NjIgMTUuMTg2MyA4Ni40MjExQzE1LjA5NTEgODYuMzUzNCAxNC45NzkzIDg2LjMxOTYgMTQuODM4NiA4Ni4zMTk2QzE0LjcwMDYgODYuMzE5NiAxNC41ODQ3IDg2LjM1MzQgMTQuNDkxIDg2LjQyMTFDMTQuMzk5OCA4Ni40ODYyIDE0LjMzMDggODYuNTczNSAxNC4yODM5IDg2LjY4MjlDMTQuMjM5NyA4Ni43ODk2IDE0LjIxNzUgODYuOTA0MiAxNC4yMTc1IDg3LjAyNjZaTTE2LjQ0NDEgOTAuNTMwNVY5MC4yMjU4QzE2LjQ0NDEgOTAuMDEyMyAxNi40OTEgODkuODE3IDE2LjU4NDcgODkuNjM5OUMxNi42Nzg1IDg5LjQ2MjggMTYuODEyNiA4OS4zMjA5IDE2Ljk4NzEgODkuMjE0MUMxNy4xNjE1IDg5LjEwNzMgMTcuMzY4NiA4OS4wNTQgMTcuNjA4MiA4OS4wNTRDMTcuODUyOSA4OS4wNTQgMTguMDYxMyA4OS4xMDczIDE4LjIzMzIgODkuMjE0MUMxOC40MDc2IDg5LjMyMDkgMTguNTQxOCA4OS40NjI4IDE4LjYzNTUgODkuNjM5OUMxOC43MjkzIDg5LjgxNyAxOC43NzYxIDkwLjAxMjMgMTguNzc2MSA5MC4yMjU4VjkwLjUzMDVDMTguNzc2MSA5MC43NDQxIDE4LjcyOTMgOTAuOTM5NCAxOC42MzU1IDkxLjExNjVDMTguNTQ0NCA5MS4yOTM1IDE4LjQxMTUgOTEuNDM1NSAxOC4yMzcxIDkxLjU0MjJDMTguMDY1MiA5MS42NDkgMTcuODU4MiA5MS43MDI0IDE3LjYxNiA5MS43MDI0QzE3LjM3MzggOTEuNzAyNCAxNy4xNjU0IDkxLjY0OSAxNi45OTEgOTEuNTQyMkMxNi44MTY1IDkxLjQzNTUgMTYuNjgxMSA5MS4yOTM1IDE2LjU4NDcgOTEuMTE2NUMxNi40OTEgOTAuOTM5NCAxNi40NDQxIDkwLjc0NDEgMTYuNDQ0MSA5MC41MzA1Wk0xNi45ODcxIDkwLjIyNThWOTAuNTMwNUMxNi45ODcxIDkwLjY1MDMgMTcuMDA5MiA5MC43NjQ5IDE3LjA1MzUgOTAuODc0M0MxNy4xMDAzIDkwLjk4MSAxNy4xNzA3IDkxLjA2ODMgMTcuMjY0NCA5MS4xMzZDMTcuMzU4MiA5MS4yMDExIDE3LjQ3NTMgOTEuMjMzNiAxNy42MTYgOTEuMjMzNkMxNy43NTY2IDkxLjIzMzYgMTcuODcyNSA5MS4yMDExIDE3Ljk2MzYgOTEuMTM2QzE4LjA1NzQgOTEuMDY4MyAxOC4xMjY0IDkwLjk4MSAxOC4xNzA3IDkwLjg3NDNDMTguMjE0OSA5MC43Njc1IDE4LjIzNzEgOTAuNjUyOSAxOC4yMzcxIDkwLjUzMDVWOTAuMjI1OEMxOC4yMzcxIDkwLjEwMzQgMTguMjEzNiA4OS45ODg5IDE4LjE2NjggODkuODgyMUMxOC4xMjI1IDg5Ljc3NTMgMTguMDUzNSA4OS42ODk0IDE3Ljk1OTcgODkuNjI0M0MxNy44Njg2IDg5LjU1NjYgMTcuNzUxNCA4OS41MjI3IDE3LjYwODIgODkuNTIyN0MxNy40NzAxIDg5LjUyMjcgMTcuMzU0MyA4OS41NTY2IDE3LjI2MDUgODkuNjI0M0MxNy4xNjk0IDg5LjY4OTQgMTcuMTAwMyA4OS43NzUzIDE3LjA1MzUgODkuODgyMUMxNy4wMDkyIDg5Ljk4ODkgMTYuOTg3MSA5MC4xMDM0IDE2Ljk4NzEgOTAuMjI1OFpNMTcuNzg3OCA4Ni43NDE1TDE1LjAxMDUgOTEuMTg2OEwxNC42MDQzIDkwLjkyOUwxNy4zODE2IDg2LjQ4MzZMMTcuNzg3OCA4Ni43NDE1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNOC4xOTkyMiAxMTkuMjMzVjExOS44MjdINC40NzY1NlYxMTkuMzA4TDYuMzM5ODQgMTE3LjIzM0M2LjU2OTAxIDExNi45NzggNi43NDYwOSAxMTYuNzYyIDYuODcxMDkgMTE2LjU4NUM2Ljk5ODcgMTE2LjQwNSA3LjA4NzI0IDExNi4yNDUgNy4xMzY3MiAxMTYuMTA0QzcuMTg4OCAxMTUuOTYxIDcuMjE0ODQgMTE1LjgxNSA3LjIxNDg0IDExNS42NjdDNy4yMTQ4NCAxMTUuNDc5IDcuMTc1NzggMTE1LjMxIDcuMDk3NjYgMTE1LjE1OUM3LjAyMjE0IDExNS4wMDYgNi45MTAxNiAxMTQuODgzIDYuNzYxNzIgMTE0Ljc5MkM2LjYxMzI4IDExNC43MDEgNi40MzM1OSAxMTQuNjU1IDYuMjIyNjYgMTE0LjY1NUM1Ljk3MDA1IDExNC42NTUgNS43NTkxMSAxMTQuNzA1IDUuNTg5ODQgMTE0LjgwNEM1LjQyMzE4IDExNC45IDUuMjk4MTggMTE1LjAzNSA1LjIxNDg0IDExNS4yMUM1LjEzMTUxIDExNS4zODQgNS4wODk4NCAxMTUuNTg1IDUuMDg5ODQgMTE1LjgxMkg0LjM2NzE5QzQuMzY3MTkgMTE1LjQ5MSA0LjQzNzUgMTE1LjE5OCA0LjU3ODEyIDExNC45MzNDNC43MTg3NSAxMTQuNjY3IDQuOTI3MDggMTE0LjQ1NiA1LjIwMzEyIDExNC4zQzUuNDc5MTcgMTE0LjE0MSA1LjgxOTAxIDExNC4wNjIgNi4yMjI2NiAxMTQuMDYyQzYuNTgyMDMgMTE0LjA2MiA2Ljg4OTMyIDExNC4xMjUgNy4xNDQ1MyAxMTQuMjUzQzcuMzk5NzQgMTE0LjM3OCA3LjU5NTA1IDExNC41NTUgNy43MzA0NyAxMTQuNzg0QzcuODY4NDkgMTE1LjAxMSA3LjkzNzUgMTE1LjI3NiA3LjkzNzUgMTE1LjU4MUM3LjkzNzUgMTE1Ljc0OCA3LjkwODg1IDExNS45MTcgNy44NTE1NiAxMTYuMDg5QzcuNzk2ODggMTE2LjI1OCA3LjcyMDA1IDExNi40MjcgNy42MjEwOSAxMTYuNTk3QzcuNTI0NzQgMTE2Ljc2NiA3LjQxMTQ2IDExNi45MzMgNy4yODEyNSAxMTcuMDk3QzcuMTUzNjUgMTE3LjI2MSA3LjAxNjkzIDExNy40MjIgNi44NzEwOSAxMTcuNTgxTDUuMzQ3NjYgMTE5LjIzM0g4LjE5OTIyWk0xMi42NzUyIDExNi41M1YxMTcuMzk3QzEyLjY3NTIgMTE3Ljg2NCAxMi42MzM1IDExOC4yNTcgMTIuNTUwMiAxMTguNTc3QzEyLjQ2NjggMTE4Ljg5NyAxMi4zNDcgMTE5LjE1NSAxMi4xOTA4IDExOS4zNTFDMTIuMDM0NSAxMTkuNTQ2IDExLjg0NTcgMTE5LjY4OCAxMS42MjQ0IDExOS43NzZDMTEuNDA1NiAxMTkuODYyIDExLjE1ODIgMTE5LjkwNSAxMC44ODIyIDExOS45MDVDMTAuNjYzNSAxMTkuOTA1IDEwLjQ2MTYgMTE5Ljg3OCAxMC4yNzY3IDExOS44MjNDMTAuMDkxOCAxMTkuNzY5IDkuOTI1MTcgMTE5LjY4MSA5Ljc3NjczIDExOS41NjJDOS42MzA5IDExOS40MzkgOS41MDU5IDExOS4yOCA5LjQwMTczIDExOS4wODVDOS4yOTc1NyAxMTguODkgOS4yMTgxNCAxMTguNjUzIDkuMTYzNDUgMTE4LjM3NEM5LjEwODc3IDExOC4wOTUgOS4wODE0MiAxMTcuNzcgOS4wODE0MiAxMTcuMzk3VjExNi41M0M5LjA4MTQyIDExNi4wNjQgOS4xMjMwOSAxMTUuNjc0IDkuMjA2NDIgMTE1LjM1OEM5LjI5MjM2IDExNS4wNDMgOS40MTM0NSAxMTQuNzkxIDkuNTY5NyAxMTQuNjAxQzkuNzI1OTUgMTE0LjQwOCA5LjkxMzQ1IDExNC4yNyAxMC4xMzIyIDExNC4xODdDMTAuMzUzNiAxMTQuMTAzIDEwLjYwMSAxMTQuMDYyIDEwLjg3NDQgMTE0LjA2MkMxMS4wOTU3IDExNC4wNjIgMTEuMjk4OSAxMTQuMDg5IDExLjQ4MzggMTE0LjE0NEMxMS42NzEzIDExNC4xOTYgMTEuODM3OSAxMTQuMjggMTEuOTgzOCAxMTQuMzk3QzEyLjEyOTYgMTE0LjUxMiAxMi4yNTMzIDExNC42NjYgMTIuMzU0OSAxMTQuODU4QzEyLjQ1OSAxMTUuMDQ5IDEyLjUzODUgMTE1LjI4MiAxMi41OTMxIDExNS41NThDMTIuNjQ3OCAxMTUuODM0IDEyLjY3NTIgMTE2LjE1OCAxMi42NzUyIDExNi41M1pNMTEuOTQ4NiAxMTcuNTE1VjExNi40MDlDMTEuOTQ4NiAxMTYuMTU0IDExLjkzMyAxMTUuOTMgMTEuOTAxNyAxMTUuNzM3QzExLjg3MzEgMTE1LjU0MiAxMS44MzAxIDExNS4zNzUgMTEuNzcyOCAxMTUuMjM3QzExLjcxNTUgMTE1LjA5OSAxMS42NDI2IDExNC45ODcgMTEuNTU0MSAxMTQuOTAxQzExLjQ2ODEgMTE0LjgxNSAxMS4zNjc5IDExNC43NTMgMTEuMjUzMyAxMTQuNzE0QzExLjE0MTMgMTE0LjY3MiAxMS4wMTUgMTE0LjY1MSAxMC44NzQ0IDExNC42NTFDMTAuNzAyNSAxMTQuNjUxIDEwLjU1MDIgMTE0LjY4NCAxMC40MTc0IDExNC43NDlDMTAuMjg0NSAxMTQuODEyIDEwLjE3MjYgMTE0LjkxMiAxMC4wODE0IDExNS4wNUM5Ljk5Mjg4IDExNS4xODggOS45MjUxNyAxMTUuMzY5IDkuODc4MyAxMTUuNTkzQzkuODMxNDIgMTE1LjgxNyA5LjgwNzk4IDExNi4wODkgOS44MDc5OCAxMTYuNDA5VjExNy41MTVDOS44MDc5OCAxMTcuNzcgOS44MjIzMSAxMTcuOTk1IDkuODUwOTUgMTE4LjE5QzkuODgyMiAxMTguMzg2IDkuOTI3NzggMTE4LjU1NSA5Ljk4NzY3IDExOC42OThDMTAuMDQ3NiAxMTguODM5IDEwLjEyMDUgMTE4Ljk1NSAxMC4yMDY0IDExOS4wNDZDMTAuMjkyNCAxMTkuMTM3IDEwLjM5MTMgMTE5LjIwNSAxMC41MDMzIDExOS4yNDlDMTAuNjE3OSAxMTkuMjkxIDEwLjc0NDIgMTE5LjMxMiAxMC44ODIyIDExOS4zMTJDMTEuMDU5MyAxMTkuMzEyIDExLjIxNDIgMTE5LjI3OCAxMS4zNDcgMTE5LjIxQzExLjQ3OTkgMTE5LjE0MiAxMS41OTA1IDExOS4wMzcgMTEuNjc5MSAxMTguODk0QzExLjc3MDIgMTE4Ljc0OCAxMS44Mzc5IDExOC41NjIgMTEuODgyMiAxMTguMzM1QzExLjkyNjUgMTE4LjEwNiAxMS45NDg2IDExNy44MzIgMTEuOTQ4NiAxMTcuNTE1Wk0xMy42NzQ2IDExNS41MzRWMTE1LjIzM0MxMy42NzQ2IDExNS4wMTcgMTMuNzIxNCAxMTQuODIxIDEzLjgxNTIgMTE0LjY0NEMxMy45MDg5IDExNC40NjYgMTQuMDQzMSAxMTQuMzI1IDE0LjIxNzUgMTE0LjIxOEMxNC4zOTIgMTE0LjExMSAxNC41OTkgMTE0LjA1OCAxNC44Mzg2IDExNC4wNThDMTUuMDgzNCAxMTQuMDU4IDE1LjI5MTggMTE0LjExMSAxNS40NjM2IDExNC4yMThDMTUuNjM4MSAxMTQuMzI1IDE1Ljc3MjIgMTE0LjQ2NiAxNS44NjYgMTE0LjY0NEMxNS45NTk3IDExNC44MjEgMTYuMDA2NiAxMTUuMDE3IDE2LjAwNjYgMTE1LjIzM1YxMTUuNTM0QzE2LjAwNjYgMTE1Ljc0NSAxNS45NTk3IDExNS45MzkgMTUuODY2IDExNi4xMTZDMTUuNzc0OCAxMTYuMjkzIDE1LjY0MiAxMTYuNDM1IDE1LjQ2NzUgMTE2LjU0MkMxNS4yOTU3IDExNi42NDkgMTUuMDg4NiAxMTYuNzAyIDE0Ljg0NjQgMTE2LjcwMkMxNC42MDQzIDExNi43MDIgMTQuMzk0NiAxMTYuNjQ5IDE0LjIxNzUgMTE2LjU0MkMxNC4wNDMxIDExNi40MzUgMTMuOTA4OSAxMTYuMjkzIDEzLjgxNTIgMTE2LjExNkMxMy43MjE0IDExNS45MzkgMTMuNjc0NiAxMTUuNzQ1IDEzLjY3NDYgMTE1LjUzNFpNMTQuMjE3NSAxMTUuMjMzVjExNS41MzRDMTQuMjE3NSAxMTUuNjU0IDE0LjIzOTcgMTE1Ljc2NyAxNC4yODM5IDExNS44NzRDMTQuMzMwOCAxMTUuOTgxIDE0LjQwMTEgMTE2LjA2OCAxNC40OTQ5IDExNi4xMzZDMTQuNTg4NiAxMTYuMjAxIDE0LjcwNTggMTE2LjIzMyAxNC44NDY0IDExNi4yMzNDMTQuOTg3MSAxMTYuMjMzIDE1LjEwMjkgMTE2LjIwMSAxNS4xOTQxIDExNi4xMzZDMTUuMjg1MiAxMTYuMDY4IDE1LjM1MjkgMTE1Ljk4MSAxNS4zOTcyIDExNS44NzRDMTUuNDQxNSAxMTUuNzY3IDE1LjQ2MzYgMTE1LjY1NCAxNS40NjM2IDExNS41MzRWMTE1LjIzM0MxNS40NjM2IDExNS4xMTEgMTUuNDQwMiAxMTQuOTk2IDE1LjM5MzMgMTE0Ljg5QzE1LjM0OSAxMTQuNzggMTUuMjggMTE0LjY5MyAxNS4xODYzIDExNC42MjhDMTUuMDk1MSAxMTQuNTYgMTQuOTc5MyAxMTQuNTI2IDE0LjgzODYgMTE0LjUyNkMxNC43MDA2IDExNC41MjYgMTQuNTg0NyAxMTQuNTYgMTQuNDkxIDExNC42MjhDMTQuMzk5OCAxMTQuNjkzIDE0LjMzMDggMTE0Ljc4IDE0LjI4MzkgMTE0Ljg5QzE0LjIzOTcgMTE0Ljk5NiAxNC4yMTc1IDExNS4xMTEgMTQuMjE3NSAxMTUuMjMzWk0xNi40NDQxIDExOC43MzdWMTE4LjQzM0MxNi40NDQxIDExOC4yMTkgMTYuNDkxIDExOC4wMjQgMTYuNTg0NyAxMTcuODQ3QzE2LjY3ODUgMTE3LjY3IDE2LjgxMjYgMTE3LjUyOCAxNi45ODcxIDExNy40MjFDMTcuMTYxNSAxMTcuMzE0IDE3LjM2ODYgMTE3LjI2MSAxNy42MDgyIDExNy4yNjFDMTcuODUyOSAxMTcuMjYxIDE4LjA2MTMgMTE3LjMxNCAxOC4yMzMyIDExNy40MjFDMTguNDA3NiAxMTcuNTI4IDE4LjU0MTggMTE3LjY3IDE4LjYzNTUgMTE3Ljg0N0MxOC43MjkzIDExOC4wMjQgMTguNzc2MSAxMTguMjE5IDE4Ljc3NjEgMTE4LjQzM1YxMTguNzM3QzE4Ljc3NjEgMTE4Ljk1MSAxOC43MjkzIDExOS4xNDYgMTguNjM1NSAxMTkuMzIzQzE4LjU0NDQgMTE5LjUgMTguNDExNSAxMTkuNjQyIDE4LjIzNzEgMTE5Ljc0OUMxOC4wNjUyIDExOS44NTYgMTcuODU4MiAxMTkuOTA5IDE3LjYxNiAxMTkuOTA5QzE3LjM3MzggMTE5LjkwOSAxNy4xNjU0IDExOS44NTYgMTYuOTkxIDExOS43NDlDMTYuODE2NSAxMTkuNjQyIDE2LjY4MTEgMTE5LjUgMTYuNTg0NyAxMTkuMzIzQzE2LjQ5MSAxMTkuMTQ2IDE2LjQ0NDEgMTE4Ljk1MSAxNi40NDQxIDExOC43MzdaTTE2Ljk4NzEgMTE4LjQzM1YxMTguNzM3QzE2Ljk4NzEgMTE4Ljg1NyAxNy4wMDkyIDExOC45NzIgMTcuMDUzNSAxMTkuMDgxQzE3LjEwMDMgMTE5LjE4OCAxNy4xNzA3IDExOS4yNzUgMTcuMjY0NCAxMTkuMzQzQzE3LjM1ODIgMTE5LjQwOCAxNy40NzUzIDExOS40NCAxNy42MTYgMTE5LjQ0QzE3Ljc1NjYgMTE5LjQ0IDE3Ljg3MjUgMTE5LjQwOCAxNy45NjM2IDExOS4zNDNDMTguMDU3NCAxMTkuMjc1IDE4LjEyNjQgMTE5LjE4OCAxOC4xNzA3IDExOS4wODFDMTguMjE0OSAxMTguOTc0IDE4LjIzNzEgMTE4Ljg2IDE4LjIzNzEgMTE4LjczN1YxMTguNDMzQzE4LjIzNzEgMTE4LjMxIDE4LjIxMzYgMTE4LjE5NiAxOC4xNjY4IDExOC4wODlDMTguMTIyNSAxMTcuOTgyIDE4LjA1MzUgMTE3Ljg5NiAxNy45NTk3IDExNy44MzFDMTcuODY4NiAxMTcuNzYzIDE3Ljc1MTQgMTE3LjcyOSAxNy42MDgyIDExNy43MjlDMTcuNDcwMSAxMTcuNzI5IDE3LjM1NDMgMTE3Ljc2MyAxNy4yNjA1IDExNy44MzFDMTcuMTY5NCAxMTcuODk2IDE3LjEwMDMgMTE3Ljk4MiAxNy4wNTM1IDExOC4wODlDMTcuMDA5MiAxMTguMTk2IDE2Ljk4NzEgMTE4LjMxIDE2Ljk4NzEgMTE4LjQzM1pNMTcuNzg3OCAxMTQuOTQ4TDE1LjAxMDUgMTE5LjM5NEwxNC42MDQzIDExOS4xMzZMMTcuMzgxNiAxMTQuNjlMMTcuNzg3OCAxMTQuOTQ4WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNMTMuMDQzIDE0NC43MzdWMTQ1LjYwNEMxMy4wNDMgMTQ2LjA3IDEzLjAwMTMgMTQ2LjQ2NCAxMi45MTggMTQ2Ljc4NEMxMi44MzQ2IDE0Ny4xMDQgMTIuNzE0OCAxNDcuMzYyIDEyLjU1ODYgMTQ3LjU1N0MxMi40MDIzIDE0Ny43NTMgMTIuMjEzNSAxNDcuODk1IDExLjk5MjIgMTQ3Ljk4M0MxMS43NzM0IDE0OC4wNjkgMTEuNTI2IDE0OC4xMTIgMTEuMjUgMTQ4LjExMkMxMS4wMzEyIDE0OC4xMTIgMTAuODI5NCAxNDguMDg1IDEwLjY0NDUgMTQ4LjAzQzEwLjQ1OTYgMTQ3Ljk3NSAxMC4yOTMgMTQ3Ljg4OCAxMC4xNDQ1IDE0Ny43NjhDOS45OTg3IDE0Ny42NDYgOS44NzM3IDE0Ny40ODcgOS43Njk1MyAxNDcuMjkyQzkuNjY1MzYgMTQ3LjA5NiA5LjU4NTk0IDE0Ni44NTkgOS41MzEyNSAxNDYuNTgxQzkuNDc2NTYgMTQ2LjMwMiA5LjQ0OTIyIDE0NS45NzcgOS40NDkyMiAxNDUuNjA0VjE0NC43MzdDOS40NDkyMiAxNDQuMjcxIDkuNDkwODkgMTQzLjg4IDkuNTc0MjIgMTQzLjU2NUM5LjY2MDE2IDE0My4yNSA5Ljc4MTI1IDE0Mi45OTcgOS45Mzc1IDE0Mi44MDdDMTAuMDkzOCAxNDIuNjE1IDEwLjI4MTIgMTQyLjQ3NyAxMC41IDE0Mi4zOTNDMTAuNzIxNCAxNDIuMzEgMTAuOTY4OCAxNDIuMjY4IDExLjI0MjIgMTQyLjI2OEMxMS40NjM1IDE0Mi4yNjggMTEuNjY2NyAxNDIuMjk2IDExLjg1MTYgMTQyLjM1QzEyLjAzOTEgMTQyLjQwMiAxMi4yMDU3IDE0Mi40ODcgMTIuMzUxNiAxNDIuNjA0QzEyLjQ5NzQgMTQyLjcxOSAxMi42MjExIDE0Mi44NzIgMTIuNzIyNyAxNDMuMDY1QzEyLjgyNjggMTQzLjI1NSAxMi45MDYyIDE0My40ODggMTIuOTYwOSAxNDMuNzY0QzEzLjAxNTYgMTQ0LjA0IDEzLjA0MyAxNDQuMzY1IDEzLjA0MyAxNDQuNzM3Wk0xMi4zMTY0IDE0NS43MjFWMTQ0LjYxNkMxMi4zMTY0IDE0NC4zNjEgMTIuMzAwOCAxNDQuMTM3IDEyLjI2OTUgMTQzLjk0NEMxMi4yNDA5IDE0My43NDkgMTIuMTk3OSAxNDMuNTgyIDEyLjE0MDYgMTQzLjQ0NEMxMi4wODMzIDE0My4zMDYgMTIuMDEwNCAxNDMuMTk0IDExLjkyMTkgMTQzLjEwOEMxMS44MzU5IDE0My4wMjIgMTEuNzM1NyAxNDIuOTYgMTEuNjIxMSAxNDIuOTIxQzExLjUwOTEgMTQyLjg3OSAxMS4zODI4IDE0Mi44NTggMTEuMjQyMiAxNDIuODU4QzExLjA3MDMgMTQyLjg1OCAxMC45MTggMTQyLjg5MSAxMC43ODUyIDE0Mi45NTZDMTAuNjUyMyAxNDMuMDE4IDEwLjU0MDQgMTQzLjExOSAxMC40NDkyIDE0My4yNTdDMTAuMzYwNyAxNDMuMzk1IDEwLjI5MyAxNDMuNTc2IDEwLjI0NjEgMTQzLjhDMTAuMTk5MiAxNDQuMDI0IDEwLjE3NTggMTQ0LjI5NiAxMC4xNzU4IDE0NC42MTZWMTQ1LjcyMUMxMC4xNzU4IDE0NS45NzcgMTAuMTkwMSAxNDYuMjAyIDEwLjIxODggMTQ2LjM5N0MxMC4yNSAxNDYuNTkzIDEwLjI5NTYgMTQ2Ljc2MiAxMC4zNTU1IDE0Ni45MDVDMTAuNDE1NCAxNDcuMDQ2IDEwLjQ4ODMgMTQ3LjE2MiAxMC41NzQyIDE0Ny4yNTNDMTAuNjYwMiAxNDcuMzQ0IDEwLjc1OTEgMTQ3LjQxMiAxMC44NzExIDE0Ny40NTZDMTAuOTg1NyAxNDcuNDk3IDExLjExMiAxNDcuNTE4IDExLjI1IDE0Ny41MThDMTEuNDI3MSAxNDcuNTE4IDExLjU4MiAxNDcuNDg0IDExLjcxNDggMTQ3LjQxN0MxMS44NDc3IDE0Ny4zNDkgMTEuOTU4MyAxNDcuMjQ0IDEyLjA0NjkgMTQ3LjFDMTIuMTM4IDE0Ni45NTUgMTIuMjA1NyAxNDYuNzY4IDEyLjI1IDE0Ni41NDJDMTIuMjk0MyAxNDYuMzEzIDEyLjMxNjQgMTQ2LjAzOSAxMi4zMTY0IDE0NS43MjFaTTE0LjA0MjQgMTQzLjc0MVYxNDMuNDRDMTQuMDQyNCAxNDMuMjI0IDE0LjA4OTIgMTQzLjAyNyAxNC4xODMgMTQyLjg1QzE0LjI3NjcgMTQyLjY3MyAxNC40MTA4IDE0Mi41MzEgMTQuNTg1MyAxNDIuNDI1QzE0Ljc1OTggMTQyLjMxOCAxNC45NjY4IDE0Mi4yNjQgMTUuMjA2NCAxNDIuMjY0QzE1LjQ1MTIgMTQyLjI2NCAxNS42NTk1IDE0Mi4zMTggMTUuODMxNCAxNDIuNDI1QzE2LjAwNTkgMTQyLjUzMSAxNi4xNCAxNDIuNjczIDE2LjIzMzggMTQyLjg1QzE2LjMyNzUgMTQzLjAyNyAxNi4zNzQ0IDE0My4yMjQgMTYuMzc0NCAxNDMuNDRWMTQzLjc0MUMxNi4zNzQ0IDE0My45NTIgMTYuMzI3NSAxNDQuMTQ2IDE2LjIzMzggMTQ0LjMyM0MxNi4xNDI2IDE0NC41IDE2LjAwOTggMTQ0LjY0MiAxNS44MzUzIDE0NC43NDlDMTUuNjYzNSAxNDQuODU2IDE1LjQ1NjQgMTQ0LjkwOSAxNS4yMTQyIDE0NC45MDlDMTQuOTcyIDE0NC45MDkgMTQuNzYyNCAxNDQuODU2IDE0LjU4NTMgMTQ0Ljc0OUMxNC40MTA4IDE0NC42NDIgMTQuMjc2NyAxNDQuNSAxNC4xODMgMTQ0LjMyM0MxNC4wODkyIDE0NC4xNDYgMTQuMDQyNCAxNDMuOTUyIDE0LjA0MjQgMTQzLjc0MVpNMTQuNTg1MyAxNDMuNDRWMTQzLjc0MUMxNC41ODUzIDE0My44NjEgMTQuNjA3NSAxNDMuOTc0IDE0LjY1MTcgMTQ0LjA4MUMxNC42OTg2IDE0NC4xODggMTQuNzY4OSAxNDQuMjc1IDE0Ljg2MjcgMTQ0LjM0M0MxNC45NTY0IDE0NC40MDggMTUuMDczNiAxNDQuNDQgMTUuMjE0MiAxNDQuNDRDMTUuMzU0OSAxNDQuNDQgMTUuNDcwNyAxNDQuNDA4IDE1LjU2MTkgMTQ0LjM0M0MxNS42NTMgMTQ0LjI3NSAxNS43MjA3IDE0NC4xODggMTUuNzY1IDE0NC4wODFDMTUuODA5MyAxNDMuOTc0IDE1LjgzMTQgMTQzLjg2MSAxNS44MzE0IDE0My43NDFWMTQzLjQ0QzE1LjgzMTQgMTQzLjMxOCAxNS44MDggMTQzLjIwMyAxNS43NjExIDE0My4wOTZDMTUuNzE2OCAxNDIuOTg3IDE1LjY0NzggMTQyLjkgMTUuNTU0MSAxNDIuODM1QzE1LjQ2MjkgMTQyLjc2NyAxNS4zNDcgMTQyLjczMyAxNS4yMDY0IDE0Mi43MzNDMTUuMDY4NCAxNDIuNzMzIDE0Ljk1MjUgMTQyLjc2NyAxNC44NTg4IDE0Mi44MzVDMTQuNzY3NiAxNDIuOSAxNC42OTg2IDE0Mi45ODcgMTQuNjUxNyAxNDMuMDk2QzE0LjYwNzUgMTQzLjIwMyAxNC41ODUzIDE0My4zMTggMTQuNTg1MyAxNDMuNDRaTTE2LjgxMTkgMTQ2Ljk0NFYxNDYuNjM5QzE2LjgxMTkgMTQ2LjQyNiAxNi44NTg4IDE0Ni4yMzEgMTYuOTUyNSAxNDYuMDUzQzE3LjA0NjMgMTQ1Ljg3NiAxNy4xODA0IDE0NS43MzQgMTcuMzU0OSAxNDUuNjI4QzE3LjUyOTMgMTQ1LjUyMSAxNy43MzY0IDE0NS40NjggMTcuOTc2IDE0NS40NjhDMTguMjIwNyAxNDUuNDY4IDE4LjQyOTEgMTQ1LjUyMSAxOC42MDEgMTQ1LjYyOEMxOC43NzU0IDE0NS43MzQgMTguOTA5NSAxNDUuODc2IDE5LjAwMzMgMTQ2LjA1M0MxOS4wOTcgMTQ2LjIzMSAxOS4xNDM5IDE0Ni40MjYgMTkuMTQzOSAxNDYuNjM5VjE0Ni45NDRDMTkuMTQzOSAxNDcuMTU4IDE5LjA5NyAxNDcuMzUzIDE5LjAwMzMgMTQ3LjUzQzE4LjkxMjIgMTQ3LjcwNyAxOC43NzkzIDE0Ny44NDkgMTguNjA0OSAxNDcuOTU2QzE4LjQzMyAxNDguMDYzIDE4LjIyNiAxNDguMTE2IDE3Ljk4MzggMTQ4LjExNkMxNy43NDE2IDE0OC4xMTYgMTcuNTMzMiAxNDguMDYzIDE3LjM1ODggMTQ3Ljk1NkMxNy4xODQzIDE0Ny44NDkgMTcuMDQ4OSAxNDcuNzA3IDE2Ljk1MjUgMTQ3LjUzQzE2Ljg1ODggMTQ3LjM1MyAxNi44MTE5IDE0Ny4xNTggMTYuODExOSAxNDYuOTQ0Wk0xNy4zNTQ5IDE0Ni42MzlWMTQ2Ljk0NEMxNy4zNTQ5IDE0Ny4wNjQgMTcuMzc3IDE0Ny4xNzggMTcuNDIxMyAxNDcuMjg4QzE3LjQ2ODEgMTQ3LjM5NSAxNy41Mzg1IDE0Ny40ODIgMTcuNjMyMiAxNDcuNTVDMTcuNzI2IDE0Ny42MTUgMTcuODQzMSAxNDcuNjQ3IDE3Ljk4MzggMTQ3LjY0N0MxOC4xMjQ0IDE0Ny42NDcgMTguMjQwMyAxNDcuNjE1IDE4LjMzMTQgMTQ3LjU1QzE4LjQyNTIgMTQ3LjQ4MiAxOC40OTQyIDE0Ny4zOTUgMTguNTM4NSAxNDcuMjg4QzE4LjU4MjcgMTQ3LjE4MSAxOC42MDQ5IDE0Ny4wNjYgMTguNjA0OSAxNDYuOTQ0VjE0Ni42MzlDMTguNjA0OSAxNDYuNTE3IDE4LjU4MTQgMTQ2LjQwMiAxOC41MzQ1IDE0Ni4yOTZDMTguNDkwMyAxNDYuMTg5IDE4LjQyMTMgMTQ2LjEwMyAxOC4zMjc1IDE0Ni4wMzhDMTguMjM2NCAxNDUuOTcgMTguMTE5MiAxNDUuOTM2IDE3Ljk3NiAxNDUuOTM2QzE3LjgzNzkgMTQ1LjkzNiAxNy43MjIgMTQ1Ljk3IDE3LjYyODMgMTQ2LjAzOEMxNy41MzcyIDE0Ni4xMDMgMTcuNDY4MSAxNDYuMTg5IDE3LjQyMTMgMTQ2LjI5NkMxNy4zNzcgMTQ2LjQwMiAxNy4zNTQ5IDE0Ni41MTcgMTcuMzU0OSAxNDYuNjM5Wk0xOC4xNTU2IDE0My4xNTVMMTUuMzc4MyAxNDcuNkwxNC45NzIgMTQ3LjM0M0wxNy43NDk0IDE0Mi44OTdMMTguMTU1NiAxNDMuMTU1WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8cGF0aCBkPSJNMjUgNC4xNjExM0wyMDAgNC4xNjExNiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPHBhdGggZD0iTTI1IDMzLjE2MTFMMjAwIDMzLjE2MTIiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjEyIi8+CjxwYXRoIGQ9Ik0yNSA2MS4xNjExTDIwMCA2MS4xNjEyIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC4xMiIvPgo8cGF0aCBkPSJNMjUgODkuMTYxMUwyMDAgODkuMTYxMiIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuMTIiLz4KPHBhdGggZD0iTTI1IDExOC4xNjFMMjAwIDExOC4xNjEiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjEyIi8+CjxsaW5lIHgxPSIyMy4yIiB5MT0iMTQ1Ljk2MSIgeDI9IjIwMi44IiB5Mj0iMTQ1Ljk2MSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNyIgc3Ryb2tlLXdpZHRoPSIwLjQiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAxXzQxODJfMTExOTMpIj4KPGxpbmUgeDE9IjQwLjQ1IiB5MT0iMTQ3LjA3MiIgeDI9IjQwLjQ1IiB5Mj0iMTQ2LjI1IiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNMzIuNTA5MSAxNTIuMDI1VjE1Mi44OTNDMzIuNTA5MSAxNTMuMzU5IDMyLjQ2NzQgMTUzLjc1MiAzMi4zODQxIDE1NC4wNzJDMzIuMzAwNyAxNTQuMzkzIDMyLjE4MDkgMTU0LjY1IDMyLjAyNDcgMTU0Ljg0NkMzMS44Njg0IDE1NS4wNDEgMzEuNjc5NiAxNTUuMTgzIDMxLjQ1ODMgMTU1LjI3MUMzMS4yMzk1IDE1NS4zNTcgMzAuOTkyMSAxNTUuNCAzMC43MTYxIDE1NS40QzMwLjQ5NzMgMTU1LjQgMzAuMjk1NSAxNTUuMzczIDMwLjExMDYgMTU1LjMxOEMyOS45MjU3IDE1NS4yNjQgMjkuNzU5MSAxNTUuMTc2IDI5LjYxMDYgMTU1LjA1N0MyOS40NjQ4IDE1NC45MzQgMjkuMzM5OCAxNTQuNzc1IDI5LjIzNTYgMTU0LjU4QzI5LjEzMTUgMTU0LjM4NSAyOS4wNTIgMTU0LjE0OCAyOC45OTczIDE1My44NjlDMjguOTQyNyAxNTMuNTkgMjguOTE1MyAxNTMuMjY1IDI4LjkxNTMgMTUyLjg5M1YxNTIuMDI1QzI4LjkxNTMgMTUxLjU1OSAyOC45NTcgMTUxLjE2OSAyOS4wNDAzIDE1MC44NTRDMjkuMTI2MiAxNTAuNTM4IDI5LjI0NzMgMTUwLjI4NiAyOS40MDM2IDE1MC4wOTZDMjkuNTU5OCAxNDkuOTAzIDI5Ljc0NzMgMTQ5Ljc2NSAyOS45NjYxIDE0OS42ODJDMzAuMTg3NCAxNDkuNTk4IDMwLjQzNDggMTQ5LjU1NyAzMC43MDgzIDE0OS41NTdDMzAuOTI5NiAxNDkuNTU3IDMxLjEzMjggMTQ5LjU4NCAzMS4zMTc3IDE0OS42MzlDMzEuNTA1MiAxNDkuNjkxIDMxLjY3MTggMTQ5Ljc3NSAzMS44MTc3IDE0OS44OTNDMzEuOTYzNSAxNTAuMDA3IDMyLjA4NzIgMTUwLjE2MSAzMi4xODg3IDE1MC4zNTRDMzIuMjkyOSAxNTAuNTQ0IDMyLjM3MjMgMTUwLjc3NyAzMi40MjcgMTUxLjA1M0MzMi40ODE3IDE1MS4zMjkgMzIuNTA5MSAxNTEuNjUzIDMyLjUwOTEgMTUyLjAyNVpNMzEuNzgyNSAxNTMuMDFWMTUxLjkwNEMzMS43ODI1IDE1MS42NDkgMzEuNzY2OSAxNTEuNDI1IDMxLjczNTYgMTUxLjIzMkMzMS43MDcgMTUxLjAzNyAzMS42NjQgMTUwLjg3IDMxLjYwNjcgMTUwLjczMkMzMS41NDk0IDE1MC41OTQgMzEuNDc2NSAxNTAuNDgyIDMxLjM4OCAxNTAuMzk2QzMxLjMwMiAxNTAuMzExIDMxLjIwMTggMTUwLjI0OCAzMS4wODcyIDE1MC4yMDlDMzAuOTc1MiAxNTAuMTY3IDMwLjg0ODkgMTUwLjE0NiAzMC43MDgzIDE1MC4xNDZDMzAuNTM2NCAxNTAuMTQ2IDMwLjM4NDEgMTUwLjE3OSAzMC4yNTEyIDE1MC4yNDRDMzAuMTE4NCAxNTAuMzA3IDMwLjAwNjUgMTUwLjQwNyAyOS45MTUzIDE1MC41NDVDMjkuODI2OCAxNTAuNjgzIDI5Ljc1OTEgMTUwLjg2NCAyOS43MTIyIDE1MS4wODhDMjkuNjY1MyAxNTEuMzEyIDI5LjY0MTkgMTUxLjU4NCAyOS42NDE5IDE1MS45MDRWMTUzLjAxQzI5LjY0MTkgMTUzLjI2NSAyOS42NTYyIDE1My40OSAyOS42ODQ4IDE1My42ODZDMjkuNzE2MSAxNTMuODgxIDI5Ljc2MTcgMTU0LjA1IDI5LjgyMTYgMTU0LjE5M0MyOS44ODE1IDE1NC4zMzQgMjkuOTU0NCAxNTQuNDUgMzAuMDQwMyAxNTQuNTQxQzMwLjEyNjIgMTU0LjYzMiAzMC4yMjUyIDE1NC43IDMwLjMzNzIgMTU0Ljc0NEMzMC40NTE4IDE1NC43ODYgMzAuNTc4MSAxNTQuODA3IDMwLjcxNjEgMTU0LjgwN0MzMC44OTMyIDE1NC44MDcgMzEuMDQ4MSAxNTQuNzczIDMxLjE4MDkgMTU0LjcwNUMzMS4zMTM3IDE1NC42MzcgMzEuNDI0NCAxNTQuNTMyIDMxLjUxMyAxNTQuMzg5QzMxLjYwNDEgMTU0LjI0MyAzMS42NzE4IDE1NC4wNTcgMzEuNzE2MSAxNTMuODNDMzEuNzYwNCAxNTMuNjAxIDMxLjc4MjUgMTUzLjMyNyAzMS43ODI1IDE1My4wMVpNMzUuODk2NCAxNDkuNjA0VjE1NS4zMjJIMzUuMTczN1YxNTAuNTA2TDMzLjcxNjcgMTUxLjAzN1YxNTAuMzg1TDM1Ljc4MzEgMTQ5LjYwNEgzNS44OTY0Wk00MS4xMTI0IDE0OS42MzVWMTU1LjMyMkg0MC4zNTg1VjE0OS42MzVINDEuMTEyNFpNNDMuNDk1MiAxNTIuMTkzVjE1Mi44MTFINDAuOTQ4M1YxNTIuMTkzSDQzLjQ5NTJaTTQzLjg4MTkgMTQ5LjYzNVYxNTAuMjUySDQwLjk0ODNWMTQ5LjYzNUg0My44ODE5Wk00Ni40MjE2IDE1NS40QzQ2LjEyNzMgMTU1LjQgNDUuODYwNCAxNTUuMzUxIDQ1LjYyMDggMTU1LjI1MkM0NS4zODM4IDE1NS4xNSA0NS4xNzk0IDE1NS4wMDggNDUuMDA3NSAxNTQuODI2QzQ0LjgzODMgMTU0LjY0NCA0NC43MDgxIDE1NC40MjggNDQuNjE2OSAxNTQuMTc4QzQ0LjUyNTggMTUzLjkyOCA0NC40ODAyIDE1My42NTQgNDQuNDgwMiAxNTMuMzU3VjE1My4xOTNDNDQuNDgwMiAxNTIuODUgNDQuNTMxIDE1Mi41NDQgNDQuNjMyNSAxNTIuMjc1QzQ0LjczNDEgMTUyLjAwNSA0NC44NzIxIDE1MS43NzUgNDUuMDQ2NiAxNTEuNTg4QzQ1LjIyMTEgMTUxLjQgNDUuNDE5IDE1MS4yNTggNDUuNjQwMyAxNTEuMTYyQzQ1Ljg2MTcgMTUxLjA2NiA0Ni4wOTA5IDE1MS4wMTggNDYuMzI3OCAxNTEuMDE4QzQ2LjYyOTkgMTUxLjAxOCA0Ni44OTAzIDE1MS4wNyA0Ny4xMDkxIDE1MS4xNzRDNDcuMzMwNSAxNTEuMjc4IDQ3LjUxMTQgMTUxLjQyNCA0Ny42NTIxIDE1MS42MTFDNDcuNzkyNyAxNTEuNzk2IDQ3Ljg5NjkgMTUyLjAxNSA0Ny45NjQ2IDE1Mi4yNjhDNDguMDMyMyAxNTIuNTE4IDQ4LjA2NjEgMTUyLjc5MSA0OC4wNjYxIDE1My4wODhWMTUzLjQxMkg0NC45MDk5VjE1Mi44MjJINDcuMzQzNVYxNTIuNzY4QzQ3LjMzMzEgMTUyLjU4IDQ3LjI5NCAxNTIuMzk4IDQ3LjIyNjMgMTUyLjIyMUM0Ny4xNjEyIDE1Mi4wNDQgNDcuMDU3IDE1MS44OTggNDYuOTEzOCAxNTEuNzgzQzQ2Ljc3MDYgMTUxLjY2OSA0Ni41NzUyIDE1MS42MTEgNDYuMzI3OCAxNTEuNjExQzQ2LjE2MzggMTUxLjYxMSA0Ni4wMTI3IDE1MS42NDYgNDUuODc0NyAxNTEuNzE3QzQ1LjczNjcgMTUxLjc4NSA0NS42MTgyIDE1MS44ODYgNDUuNTE5MyAxNTIuMDIxQzQ1LjQyMDMgMTUyLjE1NyA0NS4zNDM1IDE1Mi4zMjIgNDUuMjg4OCAxNTIuNTE4QzQ1LjIzNDEgMTUyLjcxMyA0NS4yMDY4IDE1Mi45MzggNDUuMjA2OCAxNTMuMTkzVjE1My4zNTdDNDUuMjA2OCAxNTMuNTU4IDQ1LjIzNDEgMTUzLjc0NyA0NS4yODg4IDE1My45MjRDNDUuMzQ2MSAxNTQuMDk4IDQ1LjQyODEgMTU0LjI1MiA0NS41MzQ5IDE1NC4zODVDNDUuNjQ0MyAxNTQuNTE4IDQ1Ljc3NTggMTU0LjYyMiA0NS45Mjk0IDE1NC42OTdDNDYuMDg1NyAxNTQuNzczIDQ2LjI2MjcgMTU0LjgxMSA0Ni40NjA3IDE1NC44MTFDNDYuNzE1OSAxNTQuODExIDQ2LjkzMiAxNTQuNzU4IDQ3LjEwOTEgMTU0LjY1NEM0Ny4yODYyIDE1NC41NSA0Ny40NDExIDE1NC40MTEgNDcuNTczOSAxNTQuMjM2TDQ4LjAxMTQgMTU0LjU4NEM0Ny45MjAzIDE1NC43MjIgNDcuODA0NCAxNTQuODU0IDQ3LjY2MzggMTU0Ljk3OUM0Ny41MjMyIDE1NS4xMDQgNDcuMzUgMTU1LjIwNSA0Ny4xNDQzIDE1NS4yODNDNDYuOTQxMSAxNTUuMzYxIDQ2LjcwMDIgMTU1LjQgNDYuNDIxNiAxNTUuNFpNNDguOTg4NiAxNDkuMzIySDQ5LjcxNTJWMTU0LjUwMkw0OS42NTI3IDE1NS4zMjJINDguOTg4NlYxNDkuMzIyWk01Mi41NzA2IDE1My4xNzRWMTUzLjI1NkM1Mi41NzA2IDE1My41NjMgNTIuNTM0MiAxNTMuODQ4IDUyLjQ2MTMgMTU0LjExMUM1Mi4zODgzIDE1NC4zNzIgNTIuMjgxNiAxNTQuNTk4IDUyLjE0MDkgMTU0Ljc5MUM1Mi4wMDAzIDE1NC45ODQgNTEuODI4NCAxNTUuMTMzIDUxLjYyNTMgMTU1LjI0QzUxLjQyMjIgMTU1LjM0NyA1MS4xODkxIDE1NS40IDUwLjkyNjEgMTU1LjRDNTAuNjU3OSAxNTUuNCA1MC40MjIyIDE1NS4zNTUgNTAuMjE5MSAxNTUuMjY0QzUwLjAxODUgMTU1LjE3IDQ5Ljg0OTMgMTU1LjAzNiA0OS43MTEzIDE1NC44NjFDNDkuNTczMiAxNTQuNjg3IDQ5LjQ2MjYgMTU0LjQ3NiA0OS4zNzkyIDE1NC4yMjlDNDkuMjk4NSAxNTMuOTgxIDQ5LjI0MjUgMTUzLjcwMiA0OS4yMTEzIDE1My4zOTNWMTUzLjAzM0M0OS4yNDI1IDE1Mi43MjEgNDkuMjk4NSAxNTIuNDQxIDQ5LjM3OTIgMTUyLjE5M0M0OS40NjI2IDE1MS45NDYgNDkuNTczMiAxNTEuNzM1IDQ5LjcxMTMgMTUxLjU2MUM0OS44NDkzIDE1MS4zODMgNTAuMDE4NSAxNTEuMjQ5IDUwLjIxOTEgMTUxLjE1OEM1MC40MTk2IDE1MS4wNjQgNTAuNjUyNyAxNTEuMDE4IDUwLjkxODMgMTUxLjAxOEM1MS4xODM5IDE1MS4wMTggNTEuNDE5NiAxNTEuMDcgNTEuNjI1MyAxNTEuMTc0QzUxLjgzMSAxNTEuMjc1IDUyLjAwMjkgMTUxLjQyMSA1Mi4xNDA5IDE1MS42MTFDNTIuMjgxNiAxNTEuODAxIDUyLjM4ODMgMTUyLjAyOSA1Mi40NjEzIDE1Mi4yOTVDNTIuNTM0MiAxNTIuNTU4IDUyLjU3MDYgMTUyLjg1MSA1Mi41NzA2IDE1My4xNzRaTTUxLjg0NDEgMTUzLjI1NlYxNTMuMTc0QzUxLjg0NDEgMTUyLjk2MyA1MS44MjQ1IDE1Mi43NjUgNTEuNzg1NSAxNTIuNThDNTEuNzQ2NCAxNTIuMzkzIDUxLjY4MzkgMTUyLjIyOSA1MS41OTggMTUyLjA4OEM1MS41MTIgMTUxLjk0NSA1MS4zOTg4IDE1MS44MzMgNTEuMjU4MSAxNTEuNzUyQzUxLjExNzUgMTUxLjY2OSA1MC45NDQzIDE1MS42MjcgNTAuNzM4NiAxNTEuNjI3QzUwLjU1NjMgMTUxLjYyNyA1MC4zOTc1IDE1MS42NTggNTAuMjYyIDE1MS43MjFDNTAuMTI5MiAxNTEuNzgzIDUwLjAxNTkgMTUxLjg2OCA0OS45MjIyIDE1MS45NzVDNDkuODI4NCAxNTIuMDc5IDQ5Ljc1MTYgMTUyLjE5OSA0OS42OTE3IDE1Mi4zMzRDNDkuNjM0NCAxNTIuNDY3IDQ5LjU5MTUgMTUyLjYwNSA0OS41NjI4IDE1Mi43NDhWMTUzLjY4OUM0OS42MDQ1IDE1My44NzIgNDkuNjcyMiAxNTQuMDQ4IDQ5Ljc2NTkgMTU0LjIxN0M0OS44NjIzIDE1NC4zODMgNDkuOTg5OSAxNTQuNTIgNTAuMTQ4OCAxNTQuNjI3QzUwLjMxMDIgMTU0LjczNCA1MC41MDk0IDE1NC43ODcgNTAuNzQ2NCAxNTQuNzg3QzUwLjk0MTcgMTU0Ljc4NyA1MS4xMDg0IDE1NC43NDggNTEuMjQ2NCAxNTQuNjdDNTEuMzg3IDE1NC41ODkgNTEuNTAwMyAxNTQuNDc5IDUxLjU4NjMgMTU0LjMzOEM1MS42NzQ4IDE1NC4xOTcgNTEuNzM5OSAxNTQuMDM1IDUxLjc4MTYgMTUzLjg1QzUxLjgyMzIgMTUzLjY2NSA1MS44NDQxIDE1My40NjcgNTEuODQ0MSAxNTMuMjU2WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8L2c+CjxsaW5lIHgxPSI1OC4xNDk5IiB5MT0iMTQ3LjIzMyIgeDI9IjU4LjE0OTkiIHkyPSIxNDYuNDExIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDJfNDE4Ml8xMTE5MykiPgo8bGluZSB4MT0iNzUuODQ5OSIgeTE9IjE0Ny4yMzMiIHgyPSI3NS44NDk5IiB5Mj0iMTQ2LjQxMSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNSIgc3Ryb2tlLXdpZHRoPSIwLjUiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPHBhdGggZD0iTTY3LjkwOSAxNTIuMTg2VjE1My4wNTNDNjcuOTA5IDE1My41MiA2Ny44NjczIDE1My45MTMgNjcuNzg0IDE1NC4yMzNDNjcuNzAwNiAxNTQuNTUzIDY3LjU4MDggMTU0LjgxMSA2Ny40MjQ2IDE1NS4wMDdDNjcuMjY4MyAxNTUuMjAyIDY3LjA3OTUgMTU1LjM0NCA2Ni44NTgyIDE1NS40MzJDNjYuNjM5NCAxNTUuNTE4IDY2LjM5MiAxNTUuNTYxIDY2LjExNiAxNTUuNTYxQzY1Ljg5NzIgMTU1LjU2MSA2NS42OTU0IDE1NS41MzQgNjUuNTEwNSAxNTUuNDc5QzY1LjMyNTYgMTU1LjQyNSA2NS4xNTkgMTU1LjMzNyA2NS4wMTA1IDE1NS4yMThDNjQuODY0NyAxNTUuMDk1IDY0LjczOTcgMTU0LjkzNiA2NC42MzU1IDE1NC43NDFDNjQuNTMxNCAxNTQuNTQ2IDY0LjQ1MTkgMTU0LjMwOSA2NC4zOTcyIDE1NC4wM0M2NC4zNDI2IDE1My43NTEgNjQuMzE1MiAxNTMuNDI2IDY0LjMxNTIgMTUzLjA1M1YxNTIuMTg2QzY0LjMxNTIgMTUxLjcyIDY0LjM1NjkgMTUxLjMzIDY0LjQ0MDIgMTUxLjAxNEM2NC41MjYxIDE1MC42OTkgNjQuNjQ3MiAxNTAuNDQ3IDY0LjgwMzUgMTUwLjI1N0M2NC45NTk3IDE1MC4wNjQgNjUuMTQ3MiAxNDkuOTI2IDY1LjM2NiAxNDkuODQzQzY1LjU4NzMgMTQ5Ljc1OSA2NS44MzQ3IDE0OS43MTggNjYuMTA4MiAxNDkuNzE4QzY2LjMyOTUgMTQ5LjcxOCA2Ni41MzI3IDE0OS43NDUgNjYuNzE3NiAxNDkuOEM2Ni45MDUxIDE0OS44NTIgNjcuMDcxNyAxNDkuOTM2IDY3LjIxNzYgMTUwLjA1M0M2Ny4zNjM0IDE1MC4xNjggNjcuNDg3MSAxNTAuMzIyIDY3LjU4ODYgMTUwLjUxNEM2Ny42OTI4IDE1MC43MDUgNjcuNzcyMiAxNTAuOTM4IDY3LjgyNjkgMTUxLjIxNEM2Ny44ODE2IDE1MS40OSA2Ny45MDkgMTUxLjgxNCA2Ny45MDkgMTUyLjE4NlpNNjcuMTgyNCAxNTMuMTcxVjE1Mi4wNjVDNjcuMTgyNCAxNTEuODEgNjcuMTY2OCAxNTEuNTg2IDY3LjEzNTUgMTUxLjM5M0M2Ny4xMDY5IDE1MS4xOTggNjcuMDYzOSAxNTEuMDMxIDY3LjAwNjYgMTUwLjg5M0M2Ni45NDkzIDE1MC43NTUgNjYuODc2NCAxNTAuNjQzIDY2Ljc4NzkgMTUwLjU1N0M2Ni43MDE5IDE1MC40NzEgNjYuNjAxNyAxNTAuNDA5IDY2LjQ4NzEgMTUwLjM3QzY2LjM3NTEgMTUwLjMyOCA2Ni4yNDg4IDE1MC4zMDcgNjYuMTA4MiAxNTAuMzA3QzY1LjkzNjMgMTUwLjMwNyA2NS43ODQgMTUwLjM0IDY1LjY1MTEgMTUwLjQwNUM2NS41MTgzIDE1MC40NjggNjUuNDA2NCAxNTAuNTY4IDY1LjMxNTIgMTUwLjcwNkM2NS4yMjY3IDE1MC44NDQgNjUuMTU5IDE1MS4wMjUgNjUuMTEyMSAxNTEuMjQ5QzY1LjA2NTIgMTUxLjQ3MyA2NS4wNDE4IDE1MS43NDUgNjUuMDQxOCAxNTIuMDY1VjE1My4xNzFDNjUuMDQxOCAxNTMuNDI2IDY1LjA1NjEgMTUzLjY1MSA2NS4wODQ3IDE1My44NDZDNjUuMTE2IDE1NC4wNDIgNjUuMTYxNiAxNTQuMjExIDY1LjIyMTUgMTU0LjM1NEM2NS4yODE0IDE1NC40OTUgNjUuMzU0MyAxNTQuNjExIDY1LjQ0MDIgMTU0LjcwMkM2NS41MjYxIDE1NC43OTMgNjUuNjI1MSAxNTQuODYxIDY1LjczNzEgMTU0LjkwNUM2NS44NTE3IDE1NC45NDcgNjUuOTc4IDE1NC45NjggNjYuMTE2IDE1NC45NjhDNjYuMjkzMSAxNTQuOTY4IDY2LjQ0OCAxNTQuOTM0IDY2LjU4MDggMTU0Ljg2NkM2Ni43MTM2IDE1NC43OTggNjYuODI0MyAxNTQuNjkzIDY2LjkxMjkgMTU0LjU1QzY3LjAwNCAxNTQuNDA0IDY3LjA3MTcgMTU0LjIxOCA2Ny4xMTYgMTUzLjk5MUM2Ny4xNjAzIDE1My43NjIgNjcuMTgyNCAxNTMuNDg4IDY3LjE4MjQgMTUzLjE3MVpNNjkuOTc2IDE1Mi4yODRINzAuNDkxNkM3MC43NDQyIDE1Mi4yODQgNzAuOTUyNSAxNTIuMjQyIDcxLjExNjYgMTUyLjE1OUM3MS4yODMzIDE1Mi4wNzMgNzEuNDA3IDE1MS45NTcgNzEuNDg3NyAxNTEuODExQzcxLjU3MSAxNTEuNjYzIDcxLjYxMjcgMTUxLjQ5NiA3MS42MTI3IDE1MS4zMTFDNzEuNjEyNyAxNTEuMDkzIDcxLjU3NjIgMTUwLjkwOSA3MS41MDMzIDE1MC43NkM3MS40MzA0IDE1MC42MTIgNzEuMzIxIDE1MC41IDcxLjE3NTIgMTUwLjQyNUM3MS4wMjkzIDE1MC4zNDkgNzAuODQ0NSAxNTAuMzExIDcwLjYyMDUgMTUwLjMxMUM3MC40MTc0IDE1MC4zMTEgNzAuMjM3NyAxNTAuMzUyIDcwLjA4MTQgMTUwLjQzMkM2OS45Mjc4IDE1MC41MSA2OS44MDY3IDE1MC42MjIgNjkuNzE4MiAxNTAuNzY4QzY5LjYzMjIgMTUwLjkxNCA2OS41ODkyIDE1MS4wODYgNjkuNTg5MiAxNTEuMjg0SDY4Ljg2NjZDNjguODY2NiAxNTAuOTk1IDY4LjkzOTUgMTUwLjczMiA2OS4wODUzIDE1MC40OTVDNjkuMjMxMiAxNTAuMjU4IDY5LjQzNTYgMTUwLjA2OSA2OS42OTg2IDE0OS45MjhDNjkuOTY0MiAxNDkuNzg4IDcwLjI3MTUgMTQ5LjcxOCA3MC42MjA1IDE0OS43MThDNzAuOTY0MiAxNDkuNzE4IDcxLjI2NSAxNDkuNzc5IDcxLjUyMjggMTQ5LjkwMUM3MS43ODA3IDE1MC4wMjEgNzEuOTgxMiAxNTAuMjAxIDcyLjEyNDQgMTUwLjQ0QzcyLjI2NzYgMTUwLjY3NyA3Mi4zMzkyIDE1MC45NzMgNzIuMzM5MiAxNTEuMzI3QzcyLjMzOTIgMTUxLjQ3IDcyLjMwNTQgMTUxLjYyNCA3Mi4yMzc3IDE1MS43ODhDNzIuMTcyNiAxNTEuOTQ5IDcyLjA2OTcgMTUyLjEgNzEuOTI5MSAxNTIuMjQxQzcxLjc5MTEgMTUyLjM4MiA3MS42MTE0IDE1Mi40OTcgNzEuMzkgMTUyLjU4OUM3MS4xNjg3IDE1Mi42NzcgNzAuOTAzIDE1Mi43MjEgNzAuNTkzMiAxNTIuNzIxSDY5Ljk3NlYxNTIuMjg0Wk02OS45NzYgMTUyLjg3OFYxNTIuNDQ0SDcwLjU5MzJDNzAuOTU1MSAxNTIuNDQ0IDcxLjI1NDYgMTUyLjQ4NyA3MS40OTE2IDE1Mi41NzNDNzEuNzI4NiAxNTIuNjU5IDcxLjkxNDggMTUyLjc3NCA3Mi4wNTAyIDE1Mi45MTdDNzIuMTg4MiAxNTMuMDYgNzIuMjg0NiAxNTMuMjE4IDcyLjMzOTIgMTUzLjM4OUM3Mi4zOTY1IDE1My41NTkgNzIuNDI1MiAxNTMuNzI4IDcyLjQyNTIgMTUzLjg5N0M3Mi40MjUyIDE1NC4xNjMgNzIuMzc5NiAxNTQuMzk5IDcyLjI4ODUgMTU0LjYwNEM3Mi4xOTk5IDE1NC44MSA3Mi4wNzM2IDE1NC45ODQgNzEuOTA5NiAxNTUuMTI4QzcxLjc0ODEgMTU1LjI3MSA3MS41NTggMTU1LjM3OSA3MS4zMzkyIDE1NS40NTJDNzEuMTIwNSAxNTUuNTI1IDcwLjg4MjIgMTU1LjU2MSA3MC42MjQ0IDE1NS41NjFDNzAuMzc3IDE1NS41NjEgNzAuMTQzOSAxNTUuNTI2IDY5LjkyNTIgMTU1LjQ1NkM2OS43MDkgMTU1LjM4NSA2OS41MTc2IDE1NS4yODQgNjkuMzUxIDE1NS4xNTFDNjkuMTg0MyAxNTUuMDE2IDY5LjA1NDEgMTU0Ljg1IDY4Ljk2MDMgMTU0LjY1NUM2OC44NjY2IDE1NC40NTcgNjguODE5NyAxNTQuMjMyIDY4LjgxOTcgMTUzLjk3OUg2OS41NDI0QzY5LjU0MjQgMTU0LjE3NyA2OS41ODUzIDE1NC4zNSA2OS42NzEzIDE1NC40OTlDNjkuNzU5OCAxNTQuNjQ3IDY5Ljg4NDggMTU0Ljc2MyA3MC4wNDYzIDE1NC44NDZDNzAuMjEwMyAxNTQuOTI3IDcwLjQwMyAxNTQuOTY4IDcwLjYyNDQgMTU0Ljk2OEM3MC44NDU4IDE1NC45NjggNzEuMDM1OSAxNTQuOTMgNzEuMTk0NyAxNTQuODU0QzcxLjM1NjIgMTU0Ljc3NiA3MS40Nzk5IDE1NC42NTkgNzEuNTY1OCAxNTQuNTAzQzcxLjY1NDMgMTU0LjM0NiA3MS42OTg2IDE1NC4xNSA3MS42OTg2IDE1My45MTNDNzEuNjk4NiAxNTMuNjc2IDcxLjY0OTEgMTUzLjQ4MiA3MS41NTAyIDE1My4zMzFDNzEuNDUxMiAxNTMuMTc3IDcxLjMxMDYgMTUzLjA2NCA3MS4xMjgzIDE1Mi45OTFDNzAuOTQ4NiAxNTIuOTE1IDcwLjczNjQgMTUyLjg3OCA3MC40OTE2IDE1Mi44NzhINjkuOTc2Wk03Ni41MTIzIDE0OS43OTZWMTU1LjQ4M0g3NS43NTg0VjE0OS43OTZINzYuNTEyM1pNNzguODk1MSAxNTIuMzU0VjE1Mi45NzFINzYuMzQ4MlYxNTIuMzU0SDc4Ljg5NTFaTTc5LjI4MTggMTQ5Ljc5NlYxNTAuNDEzSDc2LjM0ODJWMTQ5Ljc5Nkg3OS4yODE4Wk04MS44MjE1IDE1NS41NjFDODEuNTI3MiAxNTUuNTYxIDgxLjI2MDMgMTU1LjUxMiA4MS4wMjA3IDE1NS40MTNDODAuNzgzNyAxNTUuMzExIDgwLjU3OTMgMTU1LjE2OSA4MC40MDc0IDE1NC45ODdDODAuMjM4MiAxNTQuODA1IDgwLjEwOCAxNTQuNTg5IDgwLjAxNjggMTU0LjMzOUM3OS45MjU3IDE1NC4wODkgNzkuODgwMSAxNTMuODE1IDc5Ljg4MDEgMTUzLjUxOFYxNTMuMzU0Qzc5Ljg4MDEgMTUzLjAxIDc5LjkzMDkgMTUyLjcwNSA4MC4wMzI0IDE1Mi40MzZDODAuMTM0IDE1Mi4xNjUgODAuMjcyIDE1MS45MzYgODAuNDQ2NSAxNTEuNzQ5QzgwLjYyMSAxNTEuNTYxIDgwLjgxODkgMTUxLjQxOSA4MS4wNDAyIDE1MS4zMjNDODEuMjYxNiAxNTEuMjI3IDgxLjQ5MDggMTUxLjE3OCA4MS43Mjc3IDE1MS4xNzhDODIuMDI5OCAxNTEuMTc4IDgyLjI5MDIgMTUxLjIzMSA4Mi41MDkgMTUxLjMzNUM4Mi43MzA0IDE1MS40MzkgODIuOTExMyAxNTEuNTg1IDgzLjA1MiAxNTEuNzcyQzgzLjE5MjYgMTUxLjk1NyA4My4yOTY4IDE1Mi4xNzYgODMuMzY0NSAxNTIuNDI4QzgzLjQzMjIgMTUyLjY3OCA4My40NjYgMTUyLjk1MiA4My40NjYgMTUzLjI0OVYxNTMuNTczSDgwLjMwOThWMTUyLjk4M0g4Mi43NDM0VjE1Mi45MjhDODIuNzMzIDE1Mi43NDEgODIuNjkzOSAxNTIuNTU5IDgyLjYyNjIgMTUyLjM4MkM4Mi41NjExIDE1Mi4yMDUgODIuNDU2OSAxNTIuMDU5IDgyLjMxMzcgMTUxLjk0NEM4Mi4xNzA1IDE1MS44MyA4MS45NzUxIDE1MS43NzIgODEuNzI3NyAxNTEuNzcyQzgxLjU2MzcgMTUxLjc3MiA4MS40MTI2IDE1MS44MDcgODEuMjc0NiAxNTEuODc4QzgxLjEzNjYgMTUxLjk0NSA4MS4wMTgxIDE1Mi4wNDcgODAuOTE5MiAxNTIuMTgyQzgwLjgyMDIgMTUyLjMxOCA4MC43NDM0IDE1Mi40ODMgODAuNjg4NyAxNTIuNjc4QzgwLjYzNCAxNTIuODc0IDgwLjYwNjcgMTUzLjA5OSA4MC42MDY3IDE1My4zNTRWMTUzLjUxOEM4MC42MDY3IDE1My43MTkgODAuNjM0IDE1My45MDggODAuNjg4NyAxNTQuMDg1QzgwLjc0NiAxNTQuMjU5IDgwLjgyOCAxNTQuNDEzIDgwLjkzNDggMTU0LjU0NkM4MS4wNDQyIDE1NC42NzggODEuMTc1NyAxNTQuNzgzIDgxLjMyOTMgMTU0Ljg1OEM4MS40ODU2IDE1NC45MzQgODEuNjYyNiAxNTQuOTcxIDgxLjg2MDYgMTU0Ljk3MUM4Mi4xMTU4IDE1NC45NzEgODIuMzMxOSAxNTQuOTE5IDgyLjUwOSAxNTQuODE1QzgyLjY4NjEgMTU0LjcxMSA4Mi44NDEgMTU0LjU3MiA4Mi45NzM4IDE1NC4zOTdMODMuNDExMyAxNTQuNzQ1QzgzLjMyMDIgMTU0Ljg4MyA4My4yMDQzIDE1NS4wMTQgODMuMDYzNyAxNTUuMTM5QzgyLjkyMzEgMTU1LjI2NCA4Mi43NDk5IDE1NS4zNjYgODIuNTQ0MiAxNTUuNDQ0QzgyLjM0MSAxNTUuNTIyIDgyLjEwMDEgMTU1LjU2MSA4MS44MjE1IDE1NS41NjFaTTg0LjM4ODUgMTQ5LjQ4M0g4NS4xMTUxVjE1NC42NjNMODUuMDUyNiAxNTUuNDgzSDg0LjM4ODVWMTQ5LjQ4M1pNODcuOTcwNSAxNTMuMzM1VjE1My40MTdDODcuOTcwNSAxNTMuNzI0IDg3LjkzNDEgMTU0LjAwOSA4Ny44NjEyIDE1NC4yNzJDODcuNzg4MiAxNTQuNTMzIDg3LjY4MTUgMTU0Ljc1OSA4Ny41NDA4IDE1NC45NTJDODcuNDAwMiAxNTUuMTQ1IDg3LjIyODMgMTU1LjI5NCA4Ny4wMjUyIDE1NS40MDFDODYuODIyMSAxNTUuNTA4IDg2LjU4OSAxNTUuNTYxIDg2LjMyNiAxNTUuNTYxQzg2LjA1NzggMTU1LjU2MSA4NS44MjIxIDE1NS41MTYgODUuNjE5IDE1NS40MjVDODUuNDE4NSAxNTUuMzMxIDg1LjI0OTIgMTU1LjE5NyA4NS4xMTEyIDE1NS4wMjJDODQuOTczMSAxNTQuODQ4IDg0Ljg2MjUgMTU0LjYzNyA4NC43NzkxIDE1NC4zODlDODQuNjk4NCAxNTQuMTQyIDg0LjY0MjQgMTUzLjg2MyA4NC42MTEyIDE1My41NTNWMTUzLjE5NEM4NC42NDI0IDE1Mi44ODIgODQuNjk4NCAxNTIuNjAyIDg0Ljc3OTEgMTUyLjM1NEM4NC44NjI1IDE1Mi4xMDcgODQuOTczMSAxNTEuODk2IDg1LjExMTIgMTUxLjcyMUM4NS4yNDkyIDE1MS41NDQgODUuNDE4NSAxNTEuNDEgODUuNjE5IDE1MS4zMTlDODUuODE5NSAxNTEuMjI1IDg2LjA1MjYgMTUxLjE3OCA4Ni4zMTgyIDE1MS4xNzhDODYuNTgzOCAxNTEuMTc4IDg2LjgxOTUgMTUxLjIzMSA4Ny4wMjUyIDE1MS4zMzVDODcuMjMxIDE1MS40MzYgODcuNDAyOCAxNTEuNTgyIDg3LjU0MDggMTUxLjc3MkM4Ny42ODE1IDE1MS45NjIgODcuNzg4MiAxNTIuMTkgODcuODYxMiAxNTIuNDU2Qzg3LjkzNDEgMTUyLjcxOSA4Ny45NzA1IDE1My4wMTIgODcuOTcwNSAxNTMuMzM1Wk04Ny4yNDQgMTUzLjQxN1YxNTMuMzM1Qzg3LjI0NCAxNTMuMTI0IDg3LjIyNDQgMTUyLjkyNiA4Ny4xODU0IDE1Mi43NDFDODcuMTQ2MyAxNTIuNTUzIDg3LjA4MzggMTUyLjM4OSA4Ni45OTc5IDE1Mi4yNDlDODYuOTExOSAxNTIuMTA2IDg2Ljc5ODcgMTUxLjk5NCA4Ni42NTggMTUxLjkxM0M4Ni41MTc0IDE1MS44MyA4Ni4zNDQyIDE1MS43ODggODYuMTM4NSAxNTEuNzg4Qzg1Ljk1NjIgMTUxLjc4OCA4NS43OTc0IDE1MS44MTkgODUuNjYxOSAxNTEuODgyQzg1LjUyOTEgMTUxLjk0NCA4NS40MTU4IDE1Mi4wMjkgODUuMzIyMSAxNTIuMTM1Qzg1LjIyODMgMTUyLjI0IDg1LjE1MTUgMTUyLjM1OSA4NS4wOTE2IDE1Mi40OTVDODUuMDM0MyAxNTIuNjI4IDg0Ljk5MTQgMTUyLjc2NiA4NC45NjI3IDE1Mi45MDlWMTUzLjg1Qzg1LjAwNDQgMTU0LjAzMyA4NS4wNzIxIDE1NC4yMDggODUuMTY1OCAxNTQuMzc4Qzg1LjI2MjIgMTU0LjU0NCA4NS4zODk4IDE1NC42ODEgODUuNTQ4NyAxNTQuNzg4Qzg1LjcxMDEgMTU0Ljg5NSA4NS45MDkzIDE1NC45NDggODYuMTQ2MyAxNTQuOTQ4Qzg2LjM0MTYgMTU0Ljk0OCA4Ni41MDgzIDE1NC45MDkgODYuNjQ2MyAxNTQuODMxQzg2Ljc4NjkgMTU0Ljc1IDg2LjkwMDIgMTU0LjYzOSA4Ni45ODYyIDE1NC40OTlDODcuMDc0NyAxNTQuMzU4IDg3LjEzOTggMTU0LjE5NSA4Ny4xODE1IDE1NC4wMUM4Ny4yMjMxIDE1My44MjYgODcuMjQ0IDE1My42MjggODcuMjQ0IDE1My40MTdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjwvZz4KPGxpbmUgeDE9IjkzLjU1IiB5MT0iMTQ3LjIzMyIgeDI9IjkzLjU1IiB5Mj0iMTQ2LjQxMSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNSIgc3Ryb2tlLXdpZHRoPSIwLjUiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAzXzQxODJfMTExOTMpIj4KPGxpbmUgeDE9IjExMS4yNSIgeTE9IjE0Ny4yMzMiIHgyPSIxMTEuMjUiIHkyPSIxNDYuNDExIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNMTAzLjMwOSAxNTIuMTg2VjE1My4wNTNDMTAzLjMwOSAxNTMuNTIgMTAzLjI2NyAxNTMuOTEzIDEwMy4xODQgMTU0LjIzM0MxMDMuMTAxIDE1NC41NTMgMTAyLjk4MSAxNTQuODExIDEwMi44MjUgMTU1LjAwN0MxMDIuNjY4IDE1NS4yMDIgMTAyLjQ4IDE1NS4zNDQgMTAyLjI1OCAxNTUuNDMyQzEwMi4wNCAxNTUuNTE4IDEwMS43OTIgMTU1LjU2MSAxMDEuNTE2IDE1NS41NjFDMTAxLjI5NyAxNTUuNTYxIDEwMS4wOTYgMTU1LjUzNCAxMDAuOTExIDE1NS40NzlDMTAwLjcyNiAxNTUuNDI1IDEwMC41NTkgMTU1LjMzNyAxMDAuNDExIDE1NS4yMThDMTAwLjI2NSAxNTUuMDk1IDEwMC4xNCAxNTQuOTM2IDEwMC4wMzYgMTU0Ljc0MUM5OS45MzE1IDE1NC41NDYgOTkuODUyMSAxNTQuMzA5IDk5Ljc5NzQgMTU0LjAzQzk5Ljc0MjcgMTUzLjc1MSA5OS43MTU0IDE1My40MjYgOTkuNzE1NCAxNTMuMDUzVjE1Mi4xODZDOTkuNzE1NCAxNTEuNzIgOTkuNzU3IDE1MS4zMyA5OS44NDA0IDE1MS4wMTRDOTkuOTI2MyAxNTAuNjk5IDEwMC4wNDcgMTUwLjQ0NyAxMDAuMjA0IDE1MC4yNTdDMTAwLjM2IDE1MC4wNjQgMTAwLjU0NyAxNDkuOTI2IDEwMC43NjYgMTQ5Ljg0M0MxMDAuOTg3IDE0OS43NTkgMTAxLjIzNSAxNDkuNzE4IDEwMS41MDggMTQ5LjcxOEMxMDEuNzMgMTQ5LjcxOCAxMDEuOTMzIDE0OS43NDUgMTAyLjExOCAxNDkuOEMxMDIuMzA1IDE0OS44NTIgMTAyLjQ3MiAxNDkuOTM2IDEwMi42MTggMTUwLjA1M0MxMDIuNzY0IDE1MC4xNjggMTAyLjg4NyAxNTAuMzIyIDEwMi45ODkgMTUwLjUxNEMxMDMuMDkzIDE1MC43MDUgMTAzLjE3MiAxNTAuOTM4IDEwMy4yMjcgMTUxLjIxNEMxMDMuMjgyIDE1MS40OSAxMDMuMzA5IDE1MS44MTQgMTAzLjMwOSAxNTIuMTg2Wk0xMDIuNTgzIDE1My4xNzFWMTUyLjA2NUMxMDIuNTgzIDE1MS44MSAxMDIuNTY3IDE1MS41ODYgMTAyLjUzNiAxNTEuMzkzQzEwMi41MDcgMTUxLjE5OCAxMDIuNDY0IDE1MS4wMzEgMTAyLjQwNyAxNTAuODkzQzEwMi4zNDkgMTUwLjc1NSAxMDIuMjc3IDE1MC42NDMgMTAyLjE4OCAxNTAuNTU3QzEwMi4xMDIgMTUwLjQ3MSAxMDIuMDAyIDE1MC40MDkgMTAxLjg4NyAxNTAuMzdDMTAxLjc3NSAxNTAuMzI4IDEwMS42NDkgMTUwLjMwNyAxMDEuNTA4IDE1MC4zMDdDMTAxLjMzNiAxNTAuMzA3IDEwMS4xODQgMTUwLjM0IDEwMS4wNTEgMTUwLjQwNUMxMDAuOTE4IDE1MC40NjggMTAwLjgwNyAxNTAuNTY4IDEwMC43MTUgMTUwLjcwNkMxMDAuNjI3IDE1MC44NDQgMTAwLjU1OSAxNTEuMDI1IDEwMC41MTIgMTUxLjI0OUMxMDAuNDY1IDE1MS40NzMgMTAwLjQ0MiAxNTEuNzQ1IDEwMC40NDIgMTUyLjA2NVYxNTMuMTcxQzEwMC40NDIgMTUzLjQyNiAxMDAuNDU2IDE1My42NTEgMTAwLjQ4NSAxNTMuODQ2QzEwMC41MTYgMTU0LjA0MiAxMDAuNTYyIDE1NC4yMTEgMTAwLjYyMiAxNTQuMzU0QzEwMC42ODIgMTU0LjQ5NSAxMDAuNzU0IDE1NC42MTEgMTAwLjg0IDE1NC43MDJDMTAwLjkyNiAxNTQuNzkzIDEwMS4wMjUgMTU0Ljg2MSAxMDEuMTM3IDE1NC45MDVDMTAxLjI1MiAxNTQuOTQ3IDEwMS4zNzggMTU0Ljk2OCAxMDEuNTE2IDE1NC45NjhDMTAxLjY5MyAxNTQuOTY4IDEwMS44NDggMTU0LjkzNCAxMDEuOTgxIDE1NC44NjZDMTAyLjExNCAxNTQuNzk4IDEwMi4yMjQgMTU0LjY5MyAxMDIuMzEzIDE1NC41NUMxMDIuNDA0IDE1NC40MDQgMTAyLjQ3MiAxNTQuMjE4IDEwMi41MTYgMTUzLjk5MUMxMDIuNTYgMTUzLjc2MiAxMDIuNTgzIDE1My40ODggMTAyLjU4MyAxNTMuMTcxWk0xMDUuMjM1IDE1Mi43NzZMMTA0LjY1NyAxNTIuNjI4TDEwNC45NDMgMTQ5Ljc5NkgxMDcuODZWMTUwLjQ2NEgxMDUuNTU2TDEwNS4zODQgMTUyLjAxQzEwNS40ODggMTUxLjk1MSAxMDUuNjIgMTUxLjg5NSAxMDUuNzc4IDE1MS44NDNDMTA1Ljk0IDE1MS43OSAxMDYuMTI1IDE1MS43NjQgMTA2LjMzMyAxNTEuNzY0QzEwNi41OTYgMTUxLjc2NCAxMDYuODMyIDE1MS44MSAxMDcuMDQgMTUxLjkwMUMxMDcuMjQ5IDE1MS45OSAxMDcuNDI2IDE1Mi4xMTcgMTA3LjU3MSAxNTIuMjg0QzEwNy43MiAxNTIuNDUxIDEwNy44MzMgMTUyLjY1MSAxMDcuOTExIDE1Mi44ODVDMTA3Ljk4OSAxNTMuMTIgMTA4LjAyOCAxNTMuMzgyIDEwOC4wMjggMTUzLjY3MUMxMDguMDI4IDE1My45NDQgMTA3Ljk5MSAxNTQuMTk1IDEwNy45MTUgMTU0LjQyNUMxMDcuODQyIDE1NC42NTQgMTA3LjczMiAxNTQuODU0IDEwNy41ODMgMTU1LjAyNkMxMDcuNDM1IDE1NS4xOTUgMTA3LjI0NyAxNTUuMzI3IDEwNy4wMjEgMTU1LjQyMUMxMDYuNzk3IDE1NS41MTQgMTA2LjUzMiAxNTUuNTYxIDEwNi4yMjggMTU1LjU2MUMxMDUuOTk5IDE1NS41NjEgMTA1Ljc4MSAxNTUuNTMgMTA1LjU3NSAxNTUuNDY4QzEwNS4zNzIgMTU1LjQwMiAxMDUuMTkgMTU1LjMwNSAxMDUuMDI4IDE1NS4xNzVDMTA0Ljg3IDE1NS4wNDIgMTA0LjczOSAxNTQuODc4IDEwNC42MzggMTU0LjY4MkMxMDQuNTM5IDE1NC40ODQgMTA0LjQ3NiAxNTQuMjUzIDEwNC40NSAxNTMuOTg3SDEwNS4xMzhDMTA1LjE2OSAxNTQuMjAxIDEwNS4yMzIgMTU0LjM4IDEwNS4zMjUgMTU0LjUyNkMxMDUuNDE5IDE1NC42NzIgMTA1LjU0MSAxNTQuNzgzIDEwNS42OTMgMTU0Ljg1OEMxMDUuODQ2IDE1NC45MzEgMTA2LjAyNSAxNTQuOTY4IDEwNi4yMjggMTU0Ljk2OEMxMDYuNCAxNTQuOTY4IDEwNi41NTIgMTU0LjkzOCAxMDYuNjg1IDE1NC44NzhDMTA2LjgxOCAxNTQuODE4IDEwNi45MjkgMTU0LjczMiAxMDcuMDIxIDE1NC42MkMxMDcuMTEyIDE1NC41MDggMTA3LjE4MSAxNTQuMzcyIDEwNy4yMjggMTU0LjIxNEMxMDcuMjc3IDE1NC4wNTUgMTA3LjMwMiAxNTMuODc2IDEwNy4zMDIgMTUzLjY3OEMxMDcuMzAyIDE1My40OTkgMTA3LjI3NyAxNTMuMzMyIDEwNy4yMjggMTUzLjE3OEMxMDcuMTc4IDE1My4wMjUgMTA3LjEwNCAxNTIuODkxIDEwNy4wMDUgMTUyLjc3NkMxMDYuOTA5IDE1Mi42NjIgMTA2Ljc5IDE1Mi41NzMgMTA2LjY1IDE1Mi41MUMxMDYuNTA5IDE1Mi40NDUgMTA2LjM0NyAxNTIuNDEzIDEwNi4xNjUgMTUyLjQxM0MxMDUuOTIzIDE1Mi40MTMgMTA1LjczOSAxNTIuNDQ1IDEwNS42MTQgMTUyLjUxQzEwNS40OTIgMTUyLjU3NiAxMDUuMzY2IDE1Mi42NjQgMTA1LjIzNSAxNTIuNzc2Wk0xMTEuOTEyIDE0OS43OTZWMTU1LjQ4M0gxMTEuMTU5VjE0OS43OTZIMTExLjkxMlpNMTE0LjI5NSAxNTIuMzU0VjE1Mi45NzFIMTExLjc0OFYxNTIuMzU0SDExNC4yOTVaTTExNC42ODIgMTQ5Ljc5NlYxNTAuNDEzSDExMS43NDhWMTQ5Ljc5NkgxMTQuNjgyWk0xMTcuMjIyIDE1NS41NjFDMTE2LjkyNyAxNTUuNTYxIDExNi42NiAxNTUuNTEyIDExNi40MjEgMTU1LjQxM0MxMTYuMTg0IDE1NS4zMTEgMTE1Ljk3OSAxNTUuMTY5IDExNS44MDggMTU0Ljk4N0MxMTUuNjM4IDE1NC44MDUgMTE1LjUwOCAxNTQuNTg5IDExNS40MTcgMTU0LjMzOUMxMTUuMzI2IDE1NC4wODkgMTE1LjI4IDE1My44MTUgMTE1LjI4IDE1My41MThWMTUzLjM1NEMxMTUuMjggMTUzLjAxIDExNS4zMzEgMTUyLjcwNSAxMTUuNDMzIDE1Mi40MzZDMTE1LjUzNCAxNTIuMTY1IDExNS42NzIgMTUxLjkzNiAxMTUuODQ3IDE1MS43NDlDMTE2LjAyMSAxNTEuNTYxIDExNi4yMTkgMTUxLjQxOSAxMTYuNDQgMTUxLjMyM0MxMTYuNjYyIDE1MS4yMjcgMTE2Ljg5MSAxNTEuMTc4IDExNy4xMjggMTUxLjE3OEMxMTcuNDMgMTUxLjE3OCAxMTcuNjkgMTUxLjIzMSAxMTcuOTA5IDE1MS4zMzVDMTE4LjEzIDE1MS40MzkgMTE4LjMxMSAxNTEuNTg1IDExOC40NTIgMTUxLjc3MkMxMTguNTkzIDE1MS45NTcgMTE4LjY5NyAxNTIuMTc2IDExOC43NjUgMTUyLjQyOEMxMTguODMyIDE1Mi42NzggMTE4Ljg2NiAxNTIuOTUyIDExOC44NjYgMTUzLjI0OVYxNTMuNTczSDExNS43MVYxNTIuOTgzSDExOC4xNDRWMTUyLjkyOEMxMTguMTMzIDE1Mi43NDEgMTE4LjA5NCAxNTIuNTU5IDExOC4wMjYgMTUyLjM4MkMxMTcuOTYxIDE1Mi4yMDUgMTE3Ljg1NyAxNTIuMDU5IDExNy43MTQgMTUxLjk0NEMxMTcuNTcxIDE1MS44MyAxMTcuMzc1IDE1MS43NzIgMTE3LjEyOCAxNTEuNzcyQzExNi45NjQgMTUxLjc3MiAxMTYuODEzIDE1MS44MDcgMTE2LjY3NSAxNTEuODc4QzExNi41MzcgMTUxLjk0NSAxMTYuNDE4IDE1Mi4wNDcgMTE2LjMxOSAxNTIuMTgyQzExNi4yMiAxNTIuMzE4IDExNi4xNDQgMTUyLjQ4MyAxMTYuMDg5IDE1Mi42NzhDMTE2LjAzNCAxNTIuODc0IDExNi4wMDcgMTUzLjA5OSAxMTYuMDA3IDE1My4zNTRWMTUzLjUxOEMxMTYuMDA3IDE1My43MTkgMTE2LjAzNCAxNTMuOTA4IDExNi4wODkgMTU0LjA4NUMxMTYuMTQ2IDE1NC4yNTkgMTE2LjIyOCAxNTQuNDEzIDExNi4zMzUgMTU0LjU0NkMxMTYuNDQ0IDE1NC42NzggMTE2LjU3NiAxNTQuNzgzIDExNi43MjkgMTU0Ljg1OEMxMTYuODg2IDE1NC45MzQgMTE3LjA2MyAxNTQuOTcxIDExNy4yNjEgMTU0Ljk3MUMxMTcuNTE2IDE1NC45NzEgMTE3LjczMiAxNTQuOTE5IDExNy45MDkgMTU0LjgxNUMxMTguMDg2IDE1NC43MTEgMTE4LjI0MSAxNTQuNTcyIDExOC4zNzQgMTU0LjM5N0wxMTguODExIDE1NC43NDVDMTE4LjcyIDE1NC44ODMgMTE4LjYwNCAxNTUuMDE0IDExOC40NjQgMTU1LjEzOUMxMTguMzIzIDE1NS4yNjQgMTE4LjE1IDE1NS4zNjYgMTE3Ljk0NCAxNTUuNDQ0QzExNy43NDEgMTU1LjUyMiAxMTcuNSAxNTUuNTYxIDExNy4yMjIgMTU1LjU2MVpNMTE5Ljc4OSAxNDkuNDgzSDEyMC41MTVWMTU0LjY2M0wxMjAuNDUzIDE1NS40ODNIMTE5Ljc4OVYxNDkuNDgzWk0xMjMuMzcxIDE1My4zMzVWMTUzLjQxN0MxMjMuMzcxIDE1My43MjQgMTIzLjMzNCAxNTQuMDA5IDEyMy4yNjEgMTU0LjI3MkMxMjMuMTg4IDE1NC41MzMgMTIzLjA4MiAxNTQuNzU5IDEyMi45NDEgMTU0Ljk1MkMxMjIuOCAxNTUuMTQ1IDEyMi42MjggMTU1LjI5NCAxMjIuNDI1IDE1NS40MDFDMTIyLjIyMiAxNTUuNTA4IDEyMS45ODkgMTU1LjU2MSAxMjEuNzI2IDE1NS41NjFDMTIxLjQ1OCAxNTUuNTYxIDEyMS4yMjIgMTU1LjUxNiAxMjEuMDE5IDE1NS40MjVDMTIwLjgxOSAxNTUuMzMxIDEyMC42NDkgMTU1LjE5NyAxMjAuNTExIDE1NS4wMjJDMTIwLjM3MyAxNTQuODQ4IDEyMC4yNjMgMTU0LjYzNyAxMjAuMTc5IDE1NC4zODlDMTIwLjA5OSAxNTQuMTQyIDEyMC4wNDMgMTUzLjg2MyAxMjAuMDExIDE1My41NTNWMTUzLjE5NEMxMjAuMDQzIDE1Mi44ODIgMTIwLjA5OSAxNTIuNjAyIDEyMC4xNzkgMTUyLjM1NEMxMjAuMjYzIDE1Mi4xMDcgMTIwLjM3MyAxNTEuODk2IDEyMC41MTEgMTUxLjcyMUMxMjAuNjQ5IDE1MS41NDQgMTIwLjgxOSAxNTEuNDEgMTIxLjAxOSAxNTEuMzE5QzEyMS4yMiAxNTEuMjI1IDEyMS40NTMgMTUxLjE3OCAxMjEuNzE4IDE1MS4xNzhDMTIxLjk4NCAxNTEuMTc4IDEyMi4yMiAxNTEuMjMxIDEyMi40MjUgMTUxLjMzNUMxMjIuNjMxIDE1MS40MzYgMTIyLjgwMyAxNTEuNTgyIDEyMi45NDEgMTUxLjc3MkMxMjMuMDgyIDE1MS45NjIgMTIzLjE4OCAxNTIuMTkgMTIzLjI2MSAxNTIuNDU2QzEyMy4zMzQgMTUyLjcxOSAxMjMuMzcxIDE1My4wMTIgMTIzLjM3MSAxNTMuMzM1Wk0xMjIuNjQ0IDE1My40MTdWMTUzLjMzNUMxMjIuNjQ0IDE1My4xMjQgMTIyLjYyNSAxNTIuOTI2IDEyMi41ODYgMTUyLjc0MUMxMjIuNTQ2IDE1Mi41NTMgMTIyLjQ4NCAxNTIuMzg5IDEyMi4zOTggMTUyLjI0OUMxMjIuMzEyIDE1Mi4xMDYgMTIyLjE5OSAxNTEuOTk0IDEyMi4wNTggMTUxLjkxM0MxMjEuOTE4IDE1MS44MyAxMjEuNzQ0IDE1MS43ODggMTIxLjUzOSAxNTEuNzg4QzEyMS4zNTYgMTUxLjc4OCAxMjEuMTk4IDE1MS44MTkgMTIxLjA2MiAxNTEuODgyQzEyMC45MjkgMTUxLjk0NCAxMjAuODE2IDE1Mi4wMjkgMTIwLjcyMiAxNTIuMTM1QzEyMC42MjggMTUyLjI0IDEyMC41NTIgMTUyLjM1OSAxMjAuNDkyIDE1Mi40OTVDMTIwLjQzNCAxNTIuNjI4IDEyMC4zOTIgMTUyLjc2NiAxMjAuMzYzIDE1Mi45MDlWMTUzLjg1QzEyMC40MDUgMTU0LjAzMyAxMjAuNDcyIDE1NC4yMDggMTIwLjU2NiAxNTQuMzc4QzEyMC42NjIgMTU0LjU0NCAxMjAuNzkgMTU0LjY4MSAxMjAuOTQ5IDE1NC43ODhDMTIxLjExIDE1NC44OTUgMTIxLjMwOSAxNTQuOTQ4IDEyMS41NDYgMTU0Ljk0OEMxMjEuNzQyIDE1NC45NDggMTIxLjkwOCAxNTQuOTA5IDEyMi4wNDYgMTU0LjgzMUMxMjIuMTg3IDE1NC43NSAxMjIuMyAxNTQuNjM5IDEyMi4zODYgMTU0LjQ5OUMxMjIuNDc1IDE1NC4zNTggMTIyLjU0IDE1NC4xOTUgMTIyLjU4MiAxNTQuMDFDMTIyLjYyMyAxNTMuODI2IDEyMi42NDQgMTUzLjYyOCAxMjIuNjQ0IDE1My40MTdaIiBmaWxsPSJibGFjayIgZmlsbC1vcGFjaXR5PSIwLjU0Ii8+CjwvZz4KPGxpbmUgeDE9IjEyOC45NSIgeTE9IjE0Ny4yMzMiIHgyPSIxMjguOTUiIHkyPSIxNDYuNDExIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8ZyBjbGlwLXBhdGg9InVybCgjY2xpcDRfNDE4Ml8xMTE5MykiPgo8bGluZSB4MT0iMTQ2LjY1IiB5MT0iMTQ3LjIzMyIgeDI9IjE0Ni42NSIgeTI9IjE0Ni40MTEiIHN0cm9rZT0iYmxhY2siIHN0cm9rZS1vcGFjaXR5PSIwLjUiIHN0cm9rZS13aWR0aD0iMC41IiBzdHJva2UtbGluZWNhcD0ic3F1YXJlIi8+CjxwYXRoIGQ9Ik0xMzguNzA5IDE1Mi4xODZWMTUzLjA1M0MxMzguNzA5IDE1My41MiAxMzguNjY3IDE1My45MTMgMTM4LjU4NCAxNTQuMjMzQzEzOC41MDEgMTU0LjU1MyAxMzguMzgxIDE1NC44MTEgMTM4LjIyNSAxNTUuMDA3QzEzOC4wNjggMTU1LjIwMiAxMzcuODggMTU1LjM0NCAxMzcuNjU4IDE1NS40MzJDMTM3LjQzOSAxNTUuNTE4IDEzNy4xOTIgMTU1LjU2MSAxMzYuOTE2IDE1NS41NjFDMTM2LjY5NyAxNTUuNTYxIDEzNi40OTUgMTU1LjUzNCAxMzYuMzExIDE1NS40NzlDMTM2LjEyNiAxNTUuNDI1IDEzNS45NTkgMTU1LjMzNyAxMzUuODExIDE1NS4yMThDMTM1LjY2NSAxNTUuMDk1IDEzNS41NCAxNTQuOTM2IDEzNS40MzYgMTU0Ljc0MUMxMzUuMzMxIDE1NC41NDYgMTM1LjI1MiAxNTQuMzA5IDEzNS4xOTcgMTU0LjAzQzEzNS4xNDMgMTUzLjc1MSAxMzUuMTE1IDE1My40MjYgMTM1LjExNSAxNTMuMDUzVjE1Mi4xODZDMTM1LjExNSAxNTEuNzIgMTM1LjE1NyAxNTEuMzMgMTM1LjI0IDE1MS4wMTRDMTM1LjMyNiAxNTAuNjk5IDEzNS40NDcgMTUwLjQ0NyAxMzUuNjA0IDE1MC4yNTdDMTM1Ljc2IDE1MC4wNjQgMTM1Ljk0NyAxNDkuOTI2IDEzNi4xNjYgMTQ5Ljg0M0MxMzYuMzg3IDE0OS43NTkgMTM2LjYzNSAxNDkuNzE4IDEzNi45MDggMTQ5LjcxOEMxMzcuMTMgMTQ5LjcxOCAxMzcuMzMzIDE0OS43NDUgMTM3LjUxOCAxNDkuOEMxMzcuNzA1IDE0OS44NTIgMTM3Ljg3MiAxNDkuOTM2IDEzOC4wMTggMTUwLjA1M0MxMzguMTYzIDE1MC4xNjggMTM4LjI4NyAxNTAuMzIyIDEzOC4zODkgMTUwLjUxNEMxMzguNDkzIDE1MC43MDUgMTM4LjU3MiAxNTAuOTM4IDEzOC42MjcgMTUxLjIxNEMxMzguNjgyIDE1MS40OSAxMzguNzA5IDE1MS44MTQgMTM4LjcwOSAxNTIuMTg2Wk0xMzcuOTgyIDE1My4xNzFWMTUyLjA2NUMxMzcuOTgyIDE1MS44MSAxMzcuOTY3IDE1MS41ODYgMTM3LjkzNiAxNTEuMzkzQzEzNy45MDcgMTUxLjE5OCAxMzcuODY0IDE1MS4wMzEgMTM3LjgwNyAxNTAuODkzQzEzNy43NDkgMTUwLjc1NSAxMzcuNjc2IDE1MC42NDMgMTM3LjU4OCAxNTAuNTU3QzEzNy41MDIgMTUwLjQ3MSAxMzcuNDAyIDE1MC40MDkgMTM3LjI4NyAxNTAuMzdDMTM3LjE3NSAxNTAuMzI4IDEzNy4wNDkgMTUwLjMwNyAxMzYuOTA4IDE1MC4zMDdDMTM2LjczNiAxNTAuMzA3IDEzNi41ODQgMTUwLjM0IDEzNi40NTEgMTUwLjQwNUMxMzYuMzE4IDE1MC40NjggMTM2LjIwNiAxNTAuNTY4IDEzNi4xMTUgMTUwLjcwNkMxMzYuMDI3IDE1MC44NDQgMTM1Ljk1OSAxNTEuMDI1IDEzNS45MTIgMTUxLjI0OUMxMzUuODY1IDE1MS40NzMgMTM1Ljg0MiAxNTEuNzQ1IDEzNS44NDIgMTUyLjA2NVYxNTMuMTcxQzEzNS44NDIgMTUzLjQyNiAxMzUuODU2IDE1My42NTEgMTM1Ljg4NSAxNTMuODQ2QzEzNS45MTYgMTU0LjA0MiAxMzUuOTYyIDE1NC4yMTEgMTM2LjAyMiAxNTQuMzU0QzEzNi4wODEgMTU0LjQ5NSAxMzYuMTU0IDE1NC42MTEgMTM2LjI0IDE1NC43MDJDMTM2LjMyNiAxNTQuNzkzIDEzNi40MjUgMTU0Ljg2MSAxMzYuNTM3IDE1NC45MDVDMTM2LjY1MiAxNTQuOTQ3IDEzNi43NzggMTU0Ljk2OCAxMzYuOTE2IDE1NC45NjhDMTM3LjA5MyAxNTQuOTY4IDEzNy4yNDggMTU0LjkzNCAxMzcuMzgxIDE1NC44NjZDMTM3LjUxNCAxNTQuNzk4IDEzNy42MjQgMTU0LjY5MyAxMzcuNzEzIDE1NC41NUMxMzcuODA0IDE1NC40MDQgMTM3Ljg3MiAxNTQuMjE4IDEzNy45MTYgMTUzLjk5MUMxMzcuOTYgMTUzLjc2MiAxMzcuOTgyIDE1My40ODggMTM3Ljk4MiAxNTMuMTcxWk0xNDMuMzk3IDE0OS43OTZWMTUwLjIwMkwxNDEuMDQyIDE1NS40ODNIMTQwLjI4TDE0Mi42MzEgMTUwLjM4OUgxMzkuNTUzVjE0OS43OTZIMTQzLjM5N1pNMTQ3LjMxMiAxNDkuNzk2VjE1NS40ODNIMTQ2LjU1OFYxNDkuNzk2SDE0Ny4zMTJaTTE0OS42OTUgMTUyLjM1NFYxNTIuOTcxSDE0Ny4xNDhWMTUyLjM1NEgxNDkuNjk1Wk0xNTAuMDgyIDE0OS43OTZWMTUwLjQxM0gxNDcuMTQ4VjE0OS43OTZIMTUwLjA4MlpNMTUyLjYyMiAxNTUuNTYxQzE1Mi4zMjcgMTU1LjU2MSAxNTIuMDYgMTU1LjUxMiAxNTEuODIxIDE1NS40MTNDMTUxLjU4NCAxNTUuMzExIDE1MS4zNzkgMTU1LjE2OSAxNTEuMjA3IDE1NC45ODdDMTUxLjAzOCAxNTQuODA1IDE1MC45MDggMTU0LjU4OSAxNTAuODE3IDE1NC4zMzlDMTUwLjcyNiAxNTQuMDg5IDE1MC42OCAxNTMuODE1IDE1MC42OCAxNTMuNTE4VjE1My4zNTRDMTUwLjY4IDE1My4wMSAxNTAuNzMxIDE1Mi43MDUgMTUwLjgzMiAxNTIuNDM2QzE1MC45MzQgMTUyLjE2NSAxNTEuMDcyIDE1MS45MzYgMTUxLjI0NyAxNTEuNzQ5QzE1MS40MjEgMTUxLjU2MSAxNTEuNjE5IDE1MS40MTkgMTUxLjg0IDE1MS4zMjNDMTUyLjA2MiAxNTEuMjI3IDE1Mi4yOTEgMTUxLjE3OCAxNTIuNTI4IDE1MS4xNzhDMTUyLjgzIDE1MS4xNzggMTUzLjA5IDE1MS4yMzEgMTUzLjMwOSAxNTEuMzM1QzE1My41MyAxNTEuNDM5IDE1My43MTEgMTUxLjU4NSAxNTMuODUyIDE1MS43NzJDMTUzLjk5MyAxNTEuOTU3IDE1NC4wOTcgMTUyLjE3NiAxNTQuMTY1IDE1Mi40MjhDMTU0LjIzMiAxNTIuNjc4IDE1NC4yNjYgMTUyLjk1MiAxNTQuMjY2IDE1My4yNDlWMTUzLjU3M0gxNTEuMTFWMTUyLjk4M0gxNTMuNTQzVjE1Mi45MjhDMTUzLjUzMyAxNTIuNzQxIDE1My40OTQgMTUyLjU1OSAxNTMuNDI2IDE1Mi4zODJDMTUzLjM2MSAxNTIuMjA1IDE1My4yNTcgMTUyLjA1OSAxNTMuMTE0IDE1MS45NDRDMTUyLjk3MSAxNTEuODMgMTUyLjc3NSAxNTEuNzcyIDE1Mi41MjggMTUxLjc3MkMxNTIuMzY0IDE1MS43NzIgMTUyLjIxMyAxNTEuODA3IDE1Mi4wNzUgMTUxLjg3OEMxNTEuOTM3IDE1MS45NDUgMTUxLjgxOCAxNTIuMDQ3IDE1MS43MTkgMTUyLjE4MkMxNTEuNjIgMTUyLjMxOCAxNTEuNTQzIDE1Mi40ODMgMTUxLjQ4OSAxNTIuNjc4QzE1MS40MzQgMTUyLjg3NCAxNTEuNDA3IDE1My4wOTkgMTUxLjQwNyAxNTMuMzU0VjE1My41MThDMTUxLjQwNyAxNTMuNzE5IDE1MS40MzQgMTUzLjkwOCAxNTEuNDg5IDE1NC4wODVDMTUxLjU0NiAxNTQuMjU5IDE1MS42MjggMTU0LjQxMyAxNTEuNzM1IDE1NC41NDZDMTUxLjg0NCAxNTQuNjc4IDE1MS45NzYgMTU0Ljc4MyAxNTIuMTI5IDE1NC44NThDMTUyLjI4NiAxNTQuOTM0IDE1Mi40NjMgMTU0Ljk3MSAxNTIuNjYxIDE1NC45NzFDMTUyLjkxNiAxNTQuOTcxIDE1My4xMzIgMTU0LjkxOSAxNTMuMzA5IDE1NC44MTVDMTUzLjQ4NiAxNTQuNzExIDE1My42NDEgMTU0LjU3MiAxNTMuNzc0IDE1NC4zOTdMMTU0LjIxMSAxNTQuNzQ1QzE1NC4xMiAxNTQuODgzIDE1NC4wMDQgMTU1LjAxNCAxNTMuODY0IDE1NS4xMzlDMTUzLjcyMyAxNTUuMjY0IDE1My41NSAxNTUuMzY2IDE1My4zNDQgMTU1LjQ0NEMxNTMuMTQxIDE1NS41MjIgMTUyLjkgMTU1LjU2MSAxNTIuNjIyIDE1NS41NjFaTTE1NS4xODkgMTQ5LjQ4M0gxNTUuOTE1VjE1NC42NjNMMTU1Ljg1MyAxNTUuNDgzSDE1NS4xODlWMTQ5LjQ4M1pNMTU4Ljc3MSAxNTMuMzM1VjE1My40MTdDMTU4Ljc3MSAxNTMuNzI0IDE1OC43MzQgMTU0LjAwOSAxNTguNjYxIDE1NC4yNzJDMTU4LjU4OCAxNTQuNTMzIDE1OC40ODIgMTU0Ljc1OSAxNTguMzQxIDE1NC45NTJDMTU4LjIgMTU1LjE0NSAxNTguMDI4IDE1NS4yOTQgMTU3LjgyNSAxNTUuNDAxQzE1Ny42MjIgMTU1LjUwOCAxNTcuMzg5IDE1NS41NjEgMTU3LjEyNiAxNTUuNTYxQzE1Ni44NTggMTU1LjU2MSAxNTYuNjIyIDE1NS41MTYgMTU2LjQxOSAxNTUuNDI1QzE1Ni4yMTggMTU1LjMzMSAxNTYuMDQ5IDE1NS4xOTcgMTU1LjkxMSAxNTUuMDIyQzE1NS43NzMgMTU0Ljg0OCAxNTUuNjYzIDE1NC42MzcgMTU1LjU3OSAxNTQuMzg5QzE1NS40OTggMTU0LjE0MiAxNTUuNDQyIDE1My44NjMgMTU1LjQxMSAxNTMuNTUzVjE1My4xOTRDMTU1LjQ0MiAxNTIuODgyIDE1NS40OTggMTUyLjYwMiAxNTUuNTc5IDE1Mi4zNTRDMTU1LjY2MyAxNTIuMTA3IDE1NS43NzMgMTUxLjg5NiAxNTUuOTExIDE1MS43MjFDMTU2LjA0OSAxNTEuNTQ0IDE1Ni4yMTggMTUxLjQxIDE1Ni40MTkgMTUxLjMxOUMxNTYuNjIgMTUxLjIyNSAxNTYuODUzIDE1MS4xNzggMTU3LjExOCAxNTEuMTc4QzE1Ny4zODQgMTUxLjE3OCAxNTcuNjIgMTUxLjIzMSAxNTcuODI1IDE1MS4zMzVDMTU4LjAzMSAxNTEuNDM2IDE1OC4yMDMgMTUxLjU4MiAxNTguMzQxIDE1MS43NzJDMTU4LjQ4MiAxNTEuOTYyIDE1OC41ODggMTUyLjE5IDE1OC42NjEgMTUyLjQ1NkMxNTguNzM0IDE1Mi43MTkgMTU4Ljc3MSAxNTMuMDEyIDE1OC43NzEgMTUzLjMzNVpNMTU4LjA0NCAxNTMuNDE3VjE1My4zMzVDMTU4LjA0NCAxNTMuMTI0IDE1OC4wMjQgMTUyLjkyNiAxNTcuOTg1IDE1Mi43NDFDMTU3Ljk0NiAxNTIuNTUzIDE1Ny44ODQgMTUyLjM4OSAxNTcuNzk4IDE1Mi4yNDlDMTU3LjcxMiAxNTIuMTA2IDE1Ny41OTkgMTUxLjk5NCAxNTcuNDU4IDE1MS45MTNDMTU3LjMxNyAxNTEuODMgMTU3LjE0NCAxNTEuNzg4IDE1Ni45MzkgMTUxLjc4OEMxNTYuNzU2IDE1MS43ODggMTU2LjU5NyAxNTEuODE5IDE1Ni40NjIgMTUxLjg4MkMxNTYuMzI5IDE1MS45NDQgMTU2LjIxNiAxNTIuMDI5IDE1Ni4xMjIgMTUyLjEzNUMxNTYuMDI4IDE1Mi4yNCAxNTUuOTUyIDE1Mi4zNTkgMTU1Ljg5MiAxNTIuNDk1QzE1NS44MzQgMTUyLjYyOCAxNTUuNzkxIDE1Mi43NjYgMTU1Ljc2MyAxNTIuOTA5VjE1My44NUMxNTUuODA0IDE1NC4wMzMgMTU1Ljg3MiAxNTQuMjA4IDE1NS45NjYgMTU0LjM3OEMxNTYuMDYyIDE1NC41NDQgMTU2LjE5IDE1NC42ODEgMTU2LjM0OSAxNTQuNzg4QzE1Ni41MSAxNTQuODk1IDE1Ni43MDkgMTU0Ljk0OCAxNTYuOTQ2IDE1NC45NDhDMTU3LjE0MiAxNTQuOTQ4IDE1Ny4zMDggMTU0LjkwOSAxNTcuNDQ2IDE1NC44MzFDMTU3LjU4NyAxNTQuNzUgMTU3LjcgMTU0LjYzOSAxNTcuNzg2IDE1NC40OTlDMTU3Ljg3NSAxNTQuMzU4IDE1Ny45NCAxNTQuMTk1IDE1Ny45ODIgMTU0LjAxQzE1OC4wMjMgMTUzLjgyNiAxNTguMDQ0IDE1My42MjggMTU4LjA0NCAxNTMuNDE3WiIgZmlsbD0iYmxhY2siIGZpbGwtb3BhY2l0eT0iMC41NCIvPgo8L2c+CjxsaW5lIHgxPSIxNjQuMzUiIHkxPSIxNDcuMjMzIiB4Mj0iMTY0LjM1IiB5Mj0iMTQ2LjQxMSIgc3Ryb2tlPSJibGFjayIgc3Ryb2tlLW9wYWNpdHk9IjAuNSIgc3Ryb2tlLXdpZHRoPSIwLjUiIHN0cm9rZS1saW5lY2FwPSJzcXVhcmUiLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXA1XzQxODJfMTExOTMpIj4KPGxpbmUgeDE9IjE4Mi4wNSIgeTE9IjE0Ny4yMzMiIHgyPSIxODIuMDUiIHkyPSIxNDYuNDExIiBzdHJva2U9ImJsYWNrIiBzdHJva2Utb3BhY2l0eT0iMC41IiBzdHJva2Utd2lkdGg9IjAuNSIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIvPgo8cGF0aCBkPSJNMTc0LjEwOSAxNTIuMTg2VjE1My4wNTNDMTc0LjEwOSAxNTMuNTIgMTc0LjA2NyAxNTMuOTEzIDE3My45ODQgMTU0LjIzM0MxNzMuOTAxIDE1NC41NTMgMTczLjc4MSAxNTQuODExIDE3My42MjUgMTU1LjAwN0MxNzMuNDY5IDE1NS4yMDIgMTczLjI4IDE1NS4zNDQgMTczLjA1OCAxNTUuNDMyQzE3Mi44NCAxNTUuNTE4IDE3Mi41OTIgMTU1LjU2MSAxNzIuMzE2IDE1NS41NjFDMTcyLjA5NyAxNTUuNTYxIDE3MS44OTYgMTU1LjUzNCAxNzEuNzExIDE1NS40NzlDMTcxLjUyNiAxNTUuNDI1IDE3MS4zNTkgMTU1LjMzNyAxNzEuMjExIDE1NS4yMThDMTcxLjA2NSAxNTUuMDk1IDE3MC45NCAxNTQuOTM2IDE3MC44MzYgMTU0Ljc0MUMxNzAuNzMyIDE1NC41NDYgMTcwLjY1MiAxNTQuMzA5IDE3MC41OTcgMTU0LjAzQzE3MC41NDMgMTUzLjc1MSAxNzAuNTE1IDE1My40MjYgMTcwLjUxNSAxNTMuMDUzVjE1Mi4xODZDMTcwLjUxNSAxNTEuNzIgMTcwLjU1NyAxNTEuMzMgMTcwLjY0IDE1MS4wMTRDMTcwLjcyNiAxNTAuNjk5IDE3MC44NDcgMTUwLjQ0NyAxNzEuMDA0IDE1MC4yNTdDMTcxLjE2IDE1MC4wNjQgMTcxLjM0NyAxNDkuOTI2IDE3MS41NjYgMTQ5Ljg0M0MxNzEuNzg4IDE0OS43NTkgMTcyLjAzNSAxNDkuNzE4IDE3Mi4zMDggMTQ5LjcxOEMxNzIuNTMgMTQ5LjcxOCAxNzIuNzMzIDE0OS43NDUgMTcyLjkxOCAxNDkuOEMxNzMuMTA1IDE0OS44NTIgMTczLjI3MiAxNDkuOTM2IDE3My40MTggMTUwLjA1M0MxNzMuNTY0IDE1MC4xNjggMTczLjY4NyAxNTAuMzIyIDE3My43ODkgMTUwLjUxNEMxNzMuODkzIDE1MC43MDUgMTczLjk3MiAxNTAuOTM4IDE3NC4wMjcgMTUxLjIxNEMxNzQuMDgyIDE1MS40OSAxNzQuMTA5IDE1MS44MTQgMTc0LjEwOSAxNTIuMTg2Wk0xNzMuMzgzIDE1My4xNzFWMTUyLjA2NUMxNzMuMzgzIDE1MS44MSAxNzMuMzY3IDE1MS41ODYgMTczLjMzNiAxNTEuMzkzQzE3My4zMDcgMTUxLjE5OCAxNzMuMjY0IDE1MS4wMzEgMTczLjIwNyAxNTAuODkzQzE3My4xNSAxNTAuNzU1IDE3My4wNzcgMTUwLjY0MyAxNzIuOTg4IDE1MC41NTdDMTcyLjkwMiAxNTAuNDcxIDE3Mi44MDIgMTUwLjQwOSAxNzIuNjg3IDE1MC4zN0MxNzIuNTc1IDE1MC4zMjggMTcyLjQ0OSAxNTAuMzA3IDE3Mi4zMDggMTUwLjMwN0MxNzIuMTM2IDE1MC4zMDcgMTcxLjk4NCAxNTAuMzQgMTcxLjg1MSAxNTAuNDA1QzE3MS43MTkgMTUwLjQ2OCAxNzEuNjA3IDE1MC41NjggMTcxLjUxNSAxNTAuNzA2QzE3MS40MjcgMTUwLjg0NCAxNzEuMzU5IDE1MS4wMjUgMTcxLjMxMiAxNTEuMjQ5QzE3MS4yNjUgMTUxLjQ3MyAxNzEuMjQyIDE1MS43NDUgMTcxLjI0MiAxNTIuMDY1VjE1My4xNzFDMTcxLjI0MiAxNTMuNDI2IDE3MS4yNTYgMTUzLjY1MSAxNzEuMjg1IDE1My44NDZDMTcxLjMxNiAxNTQuMDQyIDE3MS4zNjIgMTU0LjIxMSAxNzEuNDIyIDE1NC4zNTRDMTcxLjQ4MiAxNTQuNDk1IDE3MS41NTQgMTU0LjYxMSAxNzEuNjQgMTU0LjcwMkMxNzEuNzI2IDE1NC43OTMgMTcxLjgyNSAxNTQuODYxIDE3MS45MzcgMTU0LjkwNUMxNzIuMDUyIDE1NC45NDcgMTcyLjE3OCAxNTQuOTY4IDE3Mi4zMTYgMTU0Ljk2OEMxNzIuNDkzIDE1NC45NjggMTcyLjY0OCAxNTQuOTM0IDE3Mi43ODEgMTU0Ljg2NkMxNzIuOTE0IDE1NC43OTggMTczLjAyNSAxNTQuNjkzIDE3My4xMTMgMTU0LjU1QzE3My4yMDQgMTU0LjQwNCAxNzMuMjcyIDE1NC4yMTggMTczLjMxNiAxNTMuOTkxQzE3My4zNiAxNTMuNzYyIDE3My4zODMgMTUzLjQ4OCAxNzMuMzgzIDE1My4xNzFaTTE3NS44NCAxNTQuODc4SDE3NS45MTRDMTc2LjMzMSAxNTQuODc4IDE3Ni42NyAxNTQuODE5IDE3Ni45MyAxNTQuNzAyQzE3Ny4xOSAxNTQuNTg1IDE3Ny4zOTEgMTU0LjQyNyAxNzcuNTMyIDE1NC4yMjlDMTc3LjY3MiAxNTQuMDMxIDE3Ny43NjkgMTUzLjgwOSAxNzcuODIxIDE1My41NjFDMTc3Ljg3MyAxNTMuMzExIDE3Ny44OTkgMTUzLjA1NSAxNzcuODk5IDE1Mi43OTJWMTUxLjkyMUMxNzcuODk5IDE1MS42NjMgMTc3Ljg2OSAxNTEuNDM0IDE3Ny44MDkgMTUxLjIzM0MxNzcuNzUyIDE1MS4wMzMgMTc3LjY3MSAxNTAuODY1IDE3Ny41NjcgMTUwLjcyOUMxNzcuNDY1IDE1MC41OTQgMTc3LjM0OSAxNTAuNDkxIDE3Ny4yMTkgMTUwLjQyMUMxNzcuMDg5IDE1MC4zNSAxNzYuOTUxIDE1MC4zMTUgMTc2LjgwNSAxNTAuMzE1QzE3Ni42MzggMTUwLjMxNSAxNzYuNDg5IDE1MC4zNDkgMTc2LjM1NiAxNTAuNDE3QzE3Ni4yMjYgMTUwLjQ4MiAxNzYuMTE1IDE1MC41NzQgMTc2LjAyNCAxNTAuNjk0QzE3NS45MzUgMTUwLjgxNCAxNzUuODY4IDE1MC45NTUgMTc1LjgyMSAxNTEuMTE2QzE3NS43NzQgMTUxLjI3NyAxNzUuNzUgMTUxLjQ1MyAxNzUuNzUgMTUxLjY0M0MxNzUuNzUgMTUxLjgxMyAxNzUuNzcxIDE1MS45NzcgMTc1LjgxMyAxNTIuMTM1QzE3NS44NTUgMTUyLjI5NCAxNzUuOTE4IDE1Mi40MzggMTc2LjAwNCAxNTIuNTY1QzE3Ni4wOSAxNTIuNjkzIDE3Ni4xOTcgMTUyLjc5NCAxNzYuMzI1IDE1Mi44N0MxNzYuNDU1IDE1Mi45NDMgMTc2LjYwNyAxNTIuOTc5IDE3Ni43ODIgMTUyLjk3OUMxNzYuOTQzIDE1Mi45NzkgMTc3LjA5NCAxNTIuOTQ4IDE3Ny4yMzUgMTUyLjg4NUMxNzcuMzc4IDE1Mi44MiAxNzcuNTA0IDE1Mi43MzMgMTc3LjYxNCAxNTIuNjI0QzE3Ny43MjYgMTUyLjUxMiAxNzcuODE0IDE1Mi4zODUgMTc3Ljg3OSAxNTIuMjQ1QzE3Ny45NDcgMTUyLjEwNCAxNzcuOTg2IDE1MS45NTcgMTc3Ljk5NiAxNTEuODAzSDE3OC4zNEMxNzguMzQgMTUyLjAyIDE3OC4yOTcgMTUyLjIzMyAxNzguMjExIDE1Mi40NDRDMTc4LjEyOCAxNTIuNjUyIDE3OC4wMTEgMTUyLjg0MyAxNzcuODYgMTUzLjAxNEMxNzcuNzA5IDE1My4xODYgMTc3LjUzMiAxNTMuMzI0IDE3Ny4zMjkgMTUzLjQyOEMxNzcuMTI1IDE1My41MyAxNzYuOTA0IDE1My41ODEgMTc2LjY2NCAxNTMuNTgxQzE3Ni4zODMgMTUzLjU4MSAxNzYuMTQgMTUzLjUyNiAxNzUuOTM0IDE1My40MTdDMTc1LjcyOCAxNTMuMzA3IDE3NS41NTkgMTUzLjE2MiAxNzUuNDI2IDE1Mi45NzlDMTc1LjI5NiAxNTIuNzk3IDE3NS4xOTggMTUyLjU5NCAxNzUuMTMzIDE1Mi4zN0MxNzUuMDcxIDE1Mi4xNDMgMTc1LjAzOSAxNTEuOTE0IDE3NS4wMzkgMTUxLjY4MkMxNzUuMDM5IDE1MS40MTIgMTc1LjA3NyAxNTEuMTU4IDE3NS4xNTMgMTUwLjkyMUMxNzUuMjI4IDE1MC42ODQgMTc1LjM0IDE1MC40NzUgMTc1LjQ4OSAxNTAuMjk2QzE3NS42MzcgMTUwLjExMyAxNzUuODIxIDE0OS45NzEgMTc2LjAzOSAxNDkuODdDMTc2LjI2MSAxNDkuNzY4IDE3Ni41MTYgMTQ5LjcxOCAxNzYuODA1IDE0OS43MThDMTc3LjEzMSAxNDkuNzE4IDE3Ny40MDggMTQ5Ljc4MyAxNzcuNjM3IDE0OS45MTNDMTc3Ljg2NiAxNTAuMDQzIDE3OC4wNTIgMTUwLjIxOCAxNzguMTk2IDE1MC40MzZDMTc4LjM0MiAxNTAuNjU1IDE3OC40NDggMTUwLjkwMSAxNzguNTE2IDE1MS4xNzVDMTc4LjU4NCAxNTEuNDQ4IDE3OC42MTggMTUxLjcyOSAxNzguNjE4IDE1Mi4wMThWMTUyLjI4QzE3OC42MTggMTUyLjU3NCAxNzguNTk4IDE1Mi44NzQgMTc4LjU1OSAxNTMuMTc4QzE3OC41MjMgMTUzLjQ4MSAxNzguNDUxIDE1My43NyAxNzguMzQ0IDE1NC4wNDZDMTc4LjI0IDE1NC4zMjIgMTc4LjA4OCAxNTQuNTY5IDE3Ny44ODcgMTU0Ljc4OEMxNzcuNjg3IDE1NS4wMDQgMTc3LjQyNSAxNTUuMTc2IDE3Ny4xMDIgMTU1LjMwM0MxNzYuNzgyIDE1NS40MjggMTc2LjM4NiAxNTUuNDkxIDE3NS45MTQgMTU1LjQ5MUgxNzUuODRWMTU0Ljg3OFpNMTgyLjcxMyAxNDkuNzk2VjE1NS40ODNIMTgxLjk1OVYxNDkuNzk2SDE4Mi43MTNaTTE4NS4wOTUgMTUyLjM1NFYxNTIuOTcxSDE4Mi41NDhWMTUyLjM1NEgxODUuMDk1Wk0xODUuNDgyIDE0OS43OTZWMTUwLjQxM0gxODIuNTQ4VjE0OS43OTZIMTg1LjQ4MlpNMTg4LjAyMiAxNTUuNTYxQzE4Ny43MjcgMTU1LjU2MSAxODcuNDYgMTU1LjUxMiAxODcuMjIxIDE1NS40MTNDMTg2Ljk4NCAxNTUuMzExIDE4Ni43OCAxNTUuMTY5IDE4Ni42MDggMTU0Ljk4N0MxODYuNDM4IDE1NC44MDUgMTg2LjMwOCAxNTQuNTg5IDE4Ni4yMTcgMTU0LjMzOUMxODYuMTI2IDE1NC4wODkgMTg2LjA4IDE1My44MTUgMTg2LjA4IDE1My41MThWMTUzLjM1NEMxODYuMDggMTUzLjAxIDE4Ni4xMzEgMTUyLjcwNSAxODYuMjMzIDE1Mi40MzZDMTg2LjMzNCAxNTIuMTY1IDE4Ni40NzIgMTUxLjkzNiAxODYuNjQ3IDE1MS43NDlDMTg2LjgyMSAxNTEuNTYxIDE4Ny4wMTkgMTUxLjQxOSAxODcuMjQgMTUxLjMyM0MxODcuNDYyIDE1MS4yMjcgMTg3LjY5MSAxNTEuMTc4IDE4Ny45MjggMTUxLjE3OEMxODguMjMgMTUxLjE3OCAxODguNDkgMTUxLjIzMSAxODguNzA5IDE1MS4zMzVDMTg4LjkzMSAxNTEuNDM5IDE4OS4xMTIgMTUxLjU4NSAxODkuMjUyIDE1MS43NzJDMTg5LjM5MyAxNTEuOTU3IDE4OS40OTcgMTUyLjE3NiAxODkuNTY1IDE1Mi40MjhDMTg5LjYzMiAxNTIuNjc4IDE4OS42NjYgMTUyLjk1MiAxODkuNjY2IDE1My4yNDlWMTUzLjU3M0gxODYuNTFWMTUyLjk4M0gxODguOTQ0VjE1Mi45MjhDMTg4LjkzMyAxNTIuNzQxIDE4OC44OTQgMTUyLjU1OSAxODguODI2IDE1Mi4zODJDMTg4Ljc2MSAxNTIuMjA1IDE4OC42NTcgMTUyLjA1OSAxODguNTE0IDE1MS45NDRDMTg4LjM3MSAxNTEuODMgMTg4LjE3NSAxNTEuNzcyIDE4Ny45MjggMTUxLjc3MkMxODcuNzY0IDE1MS43NzIgMTg3LjYxMyAxNTEuODA3IDE4Ny40NzUgMTUxLjg3OEMxODcuMzM3IDE1MS45NDUgMTg3LjIxOCAxNTIuMDQ3IDE4Ny4xMTkgMTUyLjE4MkMxODcuMDIgMTUyLjMxOCAxODYuOTQ0IDE1Mi40ODMgMTg2Ljg4OSAxNTIuNjc4QzE4Ni44MzQgMTUyLjg3NCAxODYuODA3IDE1My4wOTkgMTg2LjgwNyAxNTMuMzU0VjE1My41MThDMTg2LjgwNyAxNTMuNzE5IDE4Ni44MzQgMTUzLjkwOCAxODYuODg5IDE1NC4wODVDMTg2Ljk0NiAxNTQuMjU5IDE4Ny4wMjggMTU0LjQxMyAxODcuMTM1IDE1NC41NDZDMTg3LjI0NCAxNTQuNjc4IDE4Ny4zNzYgMTU0Ljc4MyAxODcuNTMgMTU0Ljg1OEMxODcuNjg2IDE1NC45MzQgMTg3Ljg2MyAxNTQuOTcxIDE4OC4wNjEgMTU0Ljk3MUMxODguMzE2IDE1NC45NzEgMTg4LjUzMiAxNTQuOTE5IDE4OC43MDkgMTU0LjgxNUMxODguODg2IDE1NC43MTEgMTg5LjA0MSAxNTQuNTcyIDE4OS4xNzQgMTU0LjM5N0wxODkuNjEyIDE1NC43NDVDMTg5LjUyIDE1NC44ODMgMTg5LjQwNSAxNTUuMDE0IDE4OS4yNjQgMTU1LjEzOUMxODkuMTIzIDE1NS4yNjQgMTg4Ljk1IDE1NS4zNjYgMTg4Ljc0NCAxNTUuNDQ0QzE4OC41NDEgMTU1LjUyMiAxODguMyAxNTUuNTYxIDE4OC4wMjIgMTU1LjU2MVpNMTkwLjU4OSAxNDkuNDgzSDE5MS4zMTVWMTU0LjY2M0wxOTEuMjUzIDE1NS40ODNIMTkwLjU4OVYxNDkuNDgzWk0xOTQuMTcxIDE1My4zMzVWMTUzLjQxN0MxOTQuMTcxIDE1My43MjQgMTk0LjEzNCAxNTQuMDA5IDE5NC4wNjEgMTU0LjI3MkMxOTMuOTg4IDE1NC41MzMgMTkzLjg4MiAxNTQuNzU5IDE5My43NDEgMTU0Ljk1MkMxOTMuNiAxNTUuMTQ1IDE5My40MjkgMTU1LjI5NCAxOTMuMjI1IDE1NS40MDFDMTkzLjAyMiAxNTUuNTA4IDE5Mi43ODkgMTU1LjU2MSAxOTIuNTI2IDE1NS41NjFDMTkyLjI1OCAxNTUuNTYxIDE5Mi4wMjIgMTU1LjUxNiAxOTEuODE5IDE1NS40MjVDMTkxLjYxOSAxNTUuMzMxIDE5MS40NDkgMTU1LjE5NyAxOTEuMzExIDE1NS4wMjJDMTkxLjE3MyAxNTQuODQ4IDE5MS4wNjMgMTU0LjYzNyAxOTAuOTc5IDE1NC4zODlDMTkwLjg5OSAxNTQuMTQyIDE5MC44NDMgMTUzLjg2MyAxOTAuODExIDE1My41NTNWMTUzLjE5NEMxOTAuODQzIDE1Mi44ODIgMTkwLjg5OSAxNTIuNjAyIDE5MC45NzkgMTUyLjM1NEMxOTEuMDYzIDE1Mi4xMDcgMTkxLjE3MyAxNTEuODk2IDE5MS4zMTEgMTUxLjcyMUMxOTEuNDQ5IDE1MS41NDQgMTkxLjYxOSAxNTEuNDEgMTkxLjgxOSAxNTEuMzE5QzE5Mi4wMiAxNTEuMjI1IDE5Mi4yNTMgMTUxLjE3OCAxOTIuNTE4IDE1MS4xNzhDMTkyLjc4NCAxNTEuMTc4IDE5My4wMiAxNTEuMjMxIDE5My4yMjUgMTUxLjMzNUMxOTMuNDMxIDE1MS40MzYgMTkzLjYwMyAxNTEuNTgyIDE5My43NDEgMTUxLjc3MkMxOTMuODgyIDE1MS45NjIgMTkzLjk4OCAxNTIuMTkgMTk0LjA2MSAxNTIuNDU2QzE5NC4xMzQgMTUyLjcxOSAxOTQuMTcxIDE1My4wMTIgMTk0LjE3MSAxNTMuMzM1Wk0xOTMuNDQ0IDE1My40MTdWMTUzLjMzNUMxOTMuNDQ0IDE1My4xMjQgMTkzLjQyNSAxNTIuOTI2IDE5My4zODYgMTUyLjc0MUMxOTMuMzQ3IDE1Mi41NTMgMTkzLjI4NCAxNTIuMzg5IDE5My4xOTggMTUyLjI0OUMxOTMuMTEyIDE1Mi4xMDYgMTkyLjk5OSAxNTEuOTk0IDE5Mi44NTggMTUxLjkxM0MxOTIuNzE4IDE1MS44MyAxOTIuNTQ0IDE1MS43ODggMTkyLjMzOSAxNTEuNzg4QzE5Mi4xNTYgMTUxLjc4OCAxOTEuOTk4IDE1MS44MTkgMTkxLjg2MiAxNTEuODgyQzE5MS43MjkgMTUxLjk0NCAxOTEuNjE2IDE1Mi4wMjkgMTkxLjUyMiAxNTIuMTM1QzE5MS40MjkgMTUyLjI0IDE5MS4zNTIgMTUyLjM1OSAxOTEuMjkyIDE1Mi40OTVDMTkxLjIzNSAxNTIuNjI4IDE5MS4xOTIgMTUyLjc2NiAxOTEuMTYzIDE1Mi45MDlWMTUzLjg1QzE5MS4yMDUgMTU0LjAzMyAxOTEuMjcyIDE1NC4yMDggMTkxLjM2NiAxNTQuMzc4QzE5MS40NjIgMTU0LjU0NCAxOTEuNTkgMTU0LjY4MSAxOTEuNzQ5IDE1NC43ODhDMTkxLjkxIDE1NC44OTUgMTkyLjExIDE1NC45NDggMTkyLjM0NyAxNTQuOTQ4QzE5Mi41NDIgMTU0Ljk0OCAxOTIuNzA4IDE1NC45MDkgMTkyLjg0NyAxNTQuODMxQzE5Mi45ODcgMTU0Ljc1IDE5My4xIDE1NC42MzkgMTkzLjE4NiAxNTQuNDk5QzE5My4yNzUgMTU0LjM1OCAxOTMuMzQgMTU0LjE5NSAxOTMuMzgyIDE1NC4wMUMxOTMuNDIzIDE1My44MjYgMTkzLjQ0NCAxNTMuNjI4IDE5My40NDQgMTUzLjQxN1oiIGZpbGw9ImJsYWNrIiBmaWxsLW9wYWNpdHk9IjAuNTQiLz4KPC9nPgo8Y2lyY2xlIGN4PSI0MSIgY3k9IjczLjE2MTEiIHI9IjMiIGZpbGw9IiMyMTk2RjMiLz4KPGNpcmNsZSBjeD0iNTkiIGN5PSI1MyIgcj0iMyIgZmlsbD0iIzIxOTZGMyIvPgo8Y2lyY2xlIGN4PSI3NyIgY3k9IjgxLjE2MTEiIHI9IjMiIGZpbGw9IiM0Q0FGNTAiLz4KPGNpcmNsZSBjeD0iOTUiIGN5PSI4NyIgcj0iMyIgZmlsbD0iIzRDQUY1MCIvPgo8Y2lyY2xlIGN4PSI0MSIgY3k9IjExMy4xNjEiIHI9IjMiIGZpbGw9IiM0Q0FGNTAiLz4KPGNpcmNsZSBjeD0iNTkiIGN5PSI5MyIgcj0iMyIgZmlsbD0iIzRDQUY1MCIvPgo8Y2lyY2xlIGN4PSIxMTIiIGN5PSI5MC4xNjExIiByPSIzIiBmaWxsPSIjNENBRjUwIi8+CjxjaXJjbGUgY3g9IjE0OCIgY3k9IjgxLjE2MTEiIHI9IjMiIGZpbGw9IiM0Q0FGNTAiLz4KPGNpcmNsZSBjeD0iMTMwIiBjeT0iMTAzIiByPSIzIiBmaWxsPSIjNENBRjUwIi8+CjxjaXJjbGUgY3g9IjE4MyIgY3k9IjEyMS4xNjEiIHI9IjMiIGZpbGw9IiM0Q0FGNTAiLz4KPGNpcmNsZSBjeD0iMTY2IiBjeT0iMTA5IiByPSIzIiBmaWxsPSIjNENBRjUwIi8+CjxjaXJjbGUgY3g9Ijc3IiBjeT0iMzciIHI9IjMiIGZpbGw9IiMyMTk2RjMiLz4KPGNpcmNsZSBjeD0iOTUiIGN5PSIzOSIgcj0iMyIgZmlsbD0iIzIxOTZGMyIvPgo8Y2lyY2xlIGN4PSIxMTIiIGN5PSIxOS4xNjExIiByPSIzIiBmaWxsPSIjMjE5NkYzIi8+CjxjaXJjbGUgY3g9IjE0OCIgY3k9IjM3IiByPSIzIiBmaWxsPSIjMjE5NkYzIi8+CjxjaXJjbGUgY3g9IjEzMCIgY3k9IjQ0IiByPSIzIiBmaWxsPSIjMjE5NkYzIi8+CjxjaXJjbGUgY3g9IjE4MyIgY3k9IjIzIiByPSIzIiBmaWxsPSIjMjE5NkYzIi8+CjxjaXJjbGUgY3g9IjE2NiIgY3k9IjQ0IiByPSIzIiBmaWxsPSIjMjE5NkYzIi8+CjxjaXJjbGUgY3g9Ijc3IiBjeT0iNzIiIHI9IjMiIGZpbGw9IiNGRkMxMDciLz4KPGNpcmNsZSBjeD0iOTUiIGN5PSI1NSIgcj0iMyIgZmlsbD0iI0ZGQzEwNyIvPgo8Y2lyY2xlIGN4PSIxMTIiIGN5PSI2Ni4xNjExIiByPSIzIiBmaWxsPSIjRkZDMTA3Ii8+CjxjaXJjbGUgY3g9IjE0OCIgY3k9IjUxLjE2MTEiIHI9IjMiIGZpbGw9IiNGRkMxMDciLz4KPGNpcmNsZSBjeD0iMTMwIiBjeT0iNzIiIHI9IjMiIGZpbGw9IiNGRkMxMDciLz4KPGNpcmNsZSBjeD0iMTgzIiBjeT0iODEiIHI9IjMiIGZpbGw9IiNGRkMxMDciLz4KPGNpcmNsZSBjeD0iMTY2IiBjeT0iNTkiIHI9IjMiIGZpbGw9IiNGRkMxMDciLz4KPGNpcmNsZSBjeD0iMjgiIGN5PSI4OSIgcj0iMyIgZmlsbD0iI0ZGQzEwNyIvPgo8Y2lyY2xlIGN4PSI0MSIgY3k9Ijk3IiByPSIzIiBmaWxsPSIjRkZDMTA3Ii8+CjxjaXJjbGUgY3g9IjU5IiBjeT0iNjguNTc4MSIgcj0iMyIgZmlsbD0iI0ZGQzEwNyIvPgo8Y2lyY2xlIGN4PSIyOCIgY3k9IjExOC4xNjEiIHI9IjMiIGZpbGw9IiMyMTk2RjMiLz4KPGNpcmNsZSBjeD0iMjgiIGN5PSIxMjkuNDg5IiByPSIzIiBmaWxsPSIjNENBRjUwIi8+CjwvZz4KPGRlZnM+CjxjbGlwUGF0aCBpZD0iY2xpcDBfNDE4Ml8xMTE5MyI+CjxyZWN0IHdpZHRoPSIyMDAiIGhlaWdodD0iMTYwIiBmaWxsPSJ3aGl0ZSIvPgo8L2NsaXBQYXRoPgo8Y2xpcFBhdGggaWQ9ImNsaXAxXzQxODJfMTExOTMiPgo8cmVjdCB3aWR0aD0iMzUuNCIgaGVpZ2h0PSIxMCIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIzIDE0Ni4xNjEpIi8+CjwvY2xpcFBhdGg+CjxjbGlwUGF0aCBpZD0iY2xpcDJfNDE4Ml8xMTE5MyI+CjxyZWN0IHdpZHRoPSIzNS40IiBoZWlnaHQ9IjEwLjMyMiIgZmlsbD0id2hpdGUiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDU4LjM5OTkgMTQ2LjE2MSkiLz4KPC9jbGlwUGF0aD4KPGNsaXBQYXRoIGlkPSJjbGlwM180MTgyXzExMTkzIj4KPHJlY3Qgd2lkdGg9IjM1LjQiIGhlaWdodD0iMTAuMzIyIiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoOTMuOCAxNDYuMTYxKSIvPgo8L2NsaXBQYXRoPgo8Y2xpcFBhdGggaWQ9ImNsaXA0XzQxODJfMTExOTMiPgo8cmVjdCB3aWR0aD0iMzUuNCIgaGVpZ2h0PSIxMC4zMjIiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxMjkuMiAxNDYuMTYxKSIvPgo8L2NsaXBQYXRoPgo8Y2xpcFBhdGggaWQ9ImNsaXA1XzQxODJfMTExOTMiPgo8cmVjdCB3aWR0aD0iMzUuNCIgaGVpZ2h0PSIxMC4zMjIiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxNjQuNiAxNDYuMTYxKSIvPgo8L2NsaXBQYXRoPgo8L2RlZnM+Cjwvc3ZnPgo=", + "description": "Displays changes to time-series data over time—for example, temperature or humidity readings.", + "descriptor": { + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "resources": [], + "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 chartType: 'point',\n previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n dataKeySettingsFunction: TbTimeSeriesChart.dataKeySettings('point'),\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries', units: '°C', decimals: 0 }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-time-series-chart-widget-settings", + "dataKeySettingsDirective": "tb-time-series-chart-key-settings", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-time-series-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":false,\"step\":false,\"stepType\":\"start\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":true,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"pointShape\":\"circle\",\"pointSize\":8,\"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)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}}},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#FFC107\",\"settings\":{\"showInLegend\":true,\"dataHiddenByDefault\":false,\"type\":\"line\",\"lineSettings\":{\"showLine\":false,\"step\":false,\"stepType\":\"start\",\"smooth\":false,\"lineType\":\"solid\",\"lineWidth\":2,\"showPoints\":true,\"showPointLabel\":false,\"pointLabelPosition\":\"top\",\"pointLabelFont\":{\"family\":\"Roboto\",\"size\":11,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"1\"},\"pointLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"pointShape\":\"circle\",\"pointSize\":8,\"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)\",\"backgroundSettings\":{\"type\":\"none\",\"opacity\":0.4,\"gradient\":{\"start\":100,\"end\":0}}}},\"_hash\":0.5534217244004682,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":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\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"top\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"yAxis\":{\"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)\",\"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)\"},\"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)\",\"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)\"},\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm:ss\",\"lastUpdateAgo\":false,\"custom\":false},\"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,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Point 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\"}" + }, + "tags": [ + "chart", + "time series", + "time-series", + "point", + "point chart" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/rpc_debug_terminal.json b/application/src/main/data/json/system/widget_types/rpc_debug_terminal.json index eed625283a..0d3409a4b4 100644 --- a/application/src/main/data/json/system/widget_types/rpc_debug_terminal.json +++ b/application/src/main/data/json/system/widget_types/rpc_debug_terminal.json @@ -11,7 +11,7 @@ "resources": [], "templateHtml": "
", "templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n\n", - "controllerScript": "var requestTimeout = 500;\nvar requestPersistent = false;\nvar persistentPollingInterval = 5000;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n if (self.ctx.settings.requestPersistent) {\n requestPersistent = self.ctx.settings.requestPersistent;\n }\n if (self.ctx.settings.persistentPollingInterval) {\n persistentPollingInterval = self.ctx.settings.persistentPollingInterval;\n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = uuidv4();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var spaceIndex = localCommand.indexOf(' ');\n if (spaceIndex === -1 && !localCommand.length) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (spaceIndex === -1) {\n spaceIndex = localCommand.length;\n }\n var name = localCommand.substr(0, spaceIndex);\n var args = localCommand.substr(spaceIndex + 1);\n if (args.length) {\n try {\n params = JSON.parse(args);\n } catch (e) {\n params = args;\n }\n }\n performRpc(this, name, params, requestUUID);\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1:]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2:]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": 2, \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestPersistent, persistentPollingInterval, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\n\nfunction uuidv4() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n \nself.onDestroy = function() {\n self.ctx.controlApi.completedCommand();\n}", + "controllerScript": "var requestTimeout = 500;\nvar requestPersistent = false;\nvar persistentPollingInterval = 5000;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n var rpcEnabled = subscription.rpcEnabled;\n var deviceName = 'Simulated';\n var prompt;\n if (subscription.targetEntityName && subscription.targetEntityName.length) {\n deviceName = subscription.targetEntityName;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n if (self.ctx.settings.requestPersistent) {\n requestPersistent = self.ctx.settings.requestPersistent;\n }\n if (self.ctx.settings.persistentPollingInterval) {\n persistentPollingInterval = self.ctx.settings.persistentPollingInterval;\n }\n var greetings = 'Welcome to ThingsBoard RPC debug terminal.\\n\\n';\n if (!rpcEnabled) {\n greetings += 'Target device is not set!\\n\\n';\n prompt = '';\n } else {\n greetings += 'Current target device for RPC commands: [[b;#fff;]' + deviceName + ']\\n\\n';\n greetings += 'Please type [[b;#fff;]\\'help\\'] to see usage.\\n';\n prompt = '[[b;#8bc34a;]' + deviceName +']> ';\n }\n \n var terminal = $('#device-terminal', self.ctx.$container).terminal(\n function(command) {\n if (command !== '') {\n try {\n var localCommand = command.trim();\n var requestUUID = uuidv4();\n if (localCommand === 'help') {\n printUsage(this);\n } else {\n var spaceIndex = localCommand.indexOf(' ');\n if (spaceIndex === -1 && !localCommand.length) {\n this.error(\"Wrong number of arguments!\");\n this.echo(' ');\n } else {\n var params;\n if (spaceIndex === -1) {\n spaceIndex = localCommand.length;\n }\n var name = localCommand.substr(0, spaceIndex);\n var args = localCommand.substr(spaceIndex + 1);\n if (args.length) {\n try {\n params = JSON.parse(args);\n } catch (e) {\n params = args;\n }\n }\n performRpc(this, name, params, requestUUID);\n }\n }\n } catch(e) {\n this.error(new String(e));\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: greetings,\n prompt: prompt,\n enabled: rpcEnabled\n });\n \n if (!rpcEnabled) {\n terminal.error('No RPC target detected!').pause();\n }\n}\n\n\nfunction printUsage(terminal) {\n var commandsListText = '\\n[[b;#fff;]Usage:]\\n';\n commandsListText += ' [params body]]\\n\\n';\n commandsListText += '[[b;#fff;]Example 1:]\\n'; \n commandsListText += ' myRemoteMethod1 myText\\n\\n'; \n commandsListText += '[[b;#fff;]Example 2:]\\n'; \n commandsListText += ' myOtherRemoteMethod \"{\\\\\"key1\\\\\": 2, \\\\\"key2\\\\\": \\\\\"myVal\\\\\"}\"\\n'; \n terminal.echo(new String(commandsListText));\n}\n\n\nfunction performRpc(terminal, method, params, requestUUID) {\n terminal.pause();\n self.ctx.controlApi.sendTwoWayCommand(method, params, requestTimeout, requestPersistent, persistentPollingInterval, requestUUID).subscribe(\n function success(responseBody) {\n terminal.echo(JSON.stringify(responseBody));\n terminal.echo(' ');\n terminal.resume();\n },\n function fail() {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.echo(' ');\n terminal.resume();\n }\n );\n}\n\n\nfunction uuidv4() {\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {\n var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);\n return v.toString(16);\n });\n}\n\n \nself.onDestroy = function() {\n self.ctx.controlApi.completedCommand();\n}", "settingsSchema": "", "dataKeySettingsSchema": "{}\n", "settingsDirective": "tb-rpc-terminal-widget-settings", diff --git a/application/src/main/data/json/system/widget_types/rpc_remote_shell.json b/application/src/main/data/json/system/widget_types/rpc_remote_shell.json index de9925a054..c02ce6b3d1 100644 --- a/application/src/main/data/json/system/widget_types/rpc_remote_shell.json +++ b/application/src/main/data/json/system/widget_types/rpc_remote_shell.json @@ -11,7 +11,7 @@ "resources": [], "templateHtml": "
", "templateCss": ".cmd .cursor.blink {\n -webkit-animation-name: terminal-underline;\n -moz-animation-name: terminal-underline;\n -ms-animation-name: terminal-underline;\n animation-name: terminal-underline;\n}\n.terminal .inverted, .cmd .inverted {\n border-bottom-color: #aaa;\n}\n", - "controllerScript": "var requestTimeout = 500;\nvar commandStatusPollingInterval = 200;\n\nvar welcome = 'Welcome to ThingsBoard RPC remote shell.\\n';\n\nvar terminal, rpcEnabled, simulated, deviceName, cwd;\nvar commandExecuting = false;\n\nself.onInit = function() {\n var subscription = self.ctx.defaultSubscription;\n rpcEnabled = subscription.rpcEnabled;\n if (subscription.targetDeviceName && subscription.targetDeviceName.length) {\n deviceName = subscription.targetDeviceName;\n } else {\n deviceName = 'Simulated';\n simulated = true;\n }\n if (self.ctx.settings.requestTimeout) {\n requestTimeout = self.ctx.settings.requestTimeout;\n }\n \n terminal = $('#device-terminal', self.ctx.$container).terminal(\n function (command) {\n if (command && command.trim().length) {\n try {\n if (simulated) {\n this.echo(command);\n } else {\n sendCommand(this, command);\n }\n } catch(e) {\n this.error(e + '');\n }\n } else {\n this.echo('');\n }\n }, {\n greetings: false,\n enabled: rpcEnabled,\n prompt: rpcEnabled ? currentPrompt : '',\n name: 'shell',\n pauseEvents: false,\n keydown: function (e, term) {\n if ((e.which == 67 || e.which == 68) && e.ctrlKey) { // CTRL+C || CTRL+D\n if (commandExecuting) {\n terminateCommand(term);\n return false;\n }\n }\n },\n onInit: initTerm\n }\n );\n \n};\n\nfunction initTerm(terminal) {\n terminal.echo(welcome);\n if (!rpcEnabled) {\n terminal.error('Target device is not set!\\n');\n } else {\n terminal.echo('Current target device for RPC terminal: [[b;#fff;]' + deviceName + ']\\n');\n if (!simulated) {\n terminal.pause();\n getTermInfo(terminal,\n function (remoteTermInfo) {\n if (remoteTermInfo) {\n terminal.echo('Remote platform info:');\n terminal.echo('OS: [[b;#fff;]' + remoteTermInfo.platform + ']');\n if (remoteTermInfo.release) {\n terminal.echo('OS release: [[b;#fff;]' + remoteTermInfo.release + ']');\n }\n terminal.echo('\\r');\n } else {\n terminal.echo('[[;#f00;]Unable to get remote platform info.\\nDevice is not responding.]\\n');\n }\n terminal.resume();\n });\n }\n }\n}\n\nfunction currentPrompt(callback) {\n if (cwd) {\n callback('[[b;#2196f3;]' + deviceName +']: [[b;#8bc34a;]' + cwd +']> ');\n } else {\n callback('[[b;#8bc34a;]' + deviceName +']> ');\n }\n}\n\nfunction getTermInfo(terminal, callback) {\n self.ctx.controlApi.sendTwoWayCommand('getTermInfo', null, requestTimeout).subscribe(\n function (termInfo) {\n cwd = termInfo.cwd;\n if (callback) {\n callback(termInfo);\n } \n },\n function () {\n if (callback) {\n callback(null);\n }\n }\n );\n}\n\nfunction sendCommand(terminal, command) {\n terminal.pause();\n var sendCommandRequest = {\n command: command,\n cwd: cwd\n };\n self.ctx.controlApi.sendTwoWayCommand('sendCommand', sendCommandRequest, requestTimeout).subscribe(\n function (responseBody) {\n if (responseBody && responseBody.ok) {\n commandExecuting = true;\n setTimeout( pollCommandStatus.bind(null,terminal), commandStatusPollingInterval );\n } else {\n var error = responseBody ? responseBody.error : 'Unhandled error.';\n terminal.error(error);\n terminal.resume();\n }\n },\n function () {\n onRpcError(terminal);\n }\n );\n}\n\nfunction terminateCommand(terminal) {\n self.ctx.controlApi.sendTwoWayCommand('terminateCommand', null, requestTimeout).subscribe(\n function (responseBody) {\n if (!responseBody.ok) {\n commandExecuting = false;\n terminal.error(responseBody.error);\n terminal.resume();\n } \n },\n function () {\n onRpcError(terminal);\n }\n ); \n}\n\nfunction onRpcError(terminal) {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.resume();\n}\n\nfunction pollCommandStatus(terminal) {\n self.ctx.controlApi.sendTwoWayCommand('getCommandStatus', null, requestTimeout).subscribe(\n function (commandStatusResponse) {\n for (var i=0;i ');\n } else {\n callback('[[b;#8bc34a;]' + deviceName +']> ');\n }\n}\n\nfunction getTermInfo(terminal, callback) {\n self.ctx.controlApi.sendTwoWayCommand('getTermInfo', null, requestTimeout).subscribe(\n function (termInfo) {\n cwd = termInfo.cwd;\n if (callback) {\n callback(termInfo);\n } \n },\n function () {\n if (callback) {\n callback(null);\n }\n }\n );\n}\n\nfunction sendCommand(terminal, command) {\n terminal.pause();\n var sendCommandRequest = {\n command: command,\n cwd: cwd\n };\n self.ctx.controlApi.sendTwoWayCommand('sendCommand', sendCommandRequest, requestTimeout).subscribe(\n function (responseBody) {\n if (responseBody && responseBody.ok) {\n commandExecuting = true;\n setTimeout( pollCommandStatus.bind(null,terminal), commandStatusPollingInterval );\n } else {\n var error = responseBody ? responseBody.error : 'Unhandled error.';\n terminal.error(error);\n terminal.resume();\n }\n },\n function () {\n onRpcError(terminal);\n }\n );\n}\n\nfunction terminateCommand(terminal) {\n self.ctx.controlApi.sendTwoWayCommand('terminateCommand', null, requestTimeout).subscribe(\n function (responseBody) {\n if (!responseBody.ok) {\n commandExecuting = false;\n terminal.error(responseBody.error);\n terminal.resume();\n } \n },\n function () {\n onRpcError(terminal);\n }\n ); \n}\n\nfunction onRpcError(terminal) {\n var errorText = self.ctx.defaultSubscription.rpcErrorText;\n terminal.error(errorText);\n terminal.resume();\n}\n\nfunction pollCommandStatus(terminal) {\n self.ctx.controlApi.sendTwoWayCommand('getCommandStatus', null, requestTimeout).subscribe(\n function (commandStatusResponse) {\n for (var i=0;i\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 previewWidth: '80%',\n embedTitlePanel: true,\n hasAdditionalLatestDataKeys: true,\n dataKeySettingsFunction: TbTimeSeriesChart.dataKeySettings(),\n defaultDataKeysFunction: function() {\n return [{ name: 'temperature', label: 'Temperature', type: 'timeseries', units: '°C', decimals: 0 }];\n }\n };\n}\n", + "settingsSchema": "{}", + "dataKeySettingsSchema": "{}", + "latestDataKeySettingsSchema": "{}", + "settingsDirective": "tb-time-series-chart-widget-settings", + "dataKeySettingsDirective": "tb-time-series-chart-key-settings", + "latestDataKeySettingsDirective": "", + "hasBasicMode": true, + "basicModeDirective": "tb-time-series-chart-basic-config", + "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":null},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#FFC107\",\"settings\":{\"type\":\"bar\"},\"_hash\":0.5534217244004682,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\",\"aggregationType\":null,\"units\":null,\"decimals\":null,\"usePostProcessing\":null,\"postFuncBody\":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\":\"AVG\",\"limit\":25000},\"timezone\":null},\"showTitle\":true,\"backgroundColor\":\"rgba(0, 0, 0, 0)\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"0px\",\"settings\":{\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"top\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"thresholds\":[],\"dataZoom\":true,\"stack\":false,\"yAxis\":{\"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)\",\"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)\"},\"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)\",\"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)\"},\"legendLabelFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"400\",\"lineHeight\":\"16px\"},\"legendLabelColor\":\"rgba(0, 0, 0, 0.76)\",\"showTooltip\":true,\"tooltipTrigger\":\"axis\",\"tooltipValueFont\":{\"family\":\"Roboto\",\"size\":12,\"sizeUnit\":\"px\",\"style\":\"normal\",\"weight\":\"500\",\"lineHeight\":\"16px\"},\"tooltipValueColor\":\"rgba(0, 0, 0, 0.76)\",\"tooltipShowDate\":true,\"tooltipDateFormat\":{\"format\":\"dd MMM yyyy HH:mm:ss\",\"lastUpdateAgo\":false,\"custom\":false},\"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,\"background\":{\"type\":\"color\",\"color\":\"#fff\",\"overlay\":{\"enabled\":false,\"color\":\"rgba(255,255,255,0.72)\",\"blur\":3}}},\"title\":\"Time series 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\"}" + }, + "tags": [ + "chart", + "time series", + "time-series", + "line", + "line chart", + "bar", + "bar chart", + "point", + "point chart" + ] +} \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/timeseries_bar_chart.json b/application/src/main/data/json/system/widget_types/timeseries_bar_chart.json index ff9c7971f0..ebb0d2680d 100644 --- a/application/src/main/data/json/system/widget_types/timeseries_bar_chart.json +++ b/application/src/main/data/json/system/widget_types/timeseries_bar_chart.json @@ -1,8 +1,8 @@ { "fqn": "charts.timeseries_bars_flot", "name": "Timeseries Bar Chart", - "deprecated": false, - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAABOFBMVEUAAAA3oPR3d3d6enp8fHyBgYGDg4OGhoaNjY2RkZGSkpKTk5OUlJSVlZWWlpaXl5eYmJiZmZmampqcnJydnZ2enp6goKChoaGioqKjo6OkpKSlpaWmpqanp6eoqKipqamqqqqrq6usrKytra2urq6wsLCxsbGysrK0tLS1tbW2tra3t7e4uLi5ubm6urq8vLy9vb2+vr6/v7/AwMDBwcHDw8PExMTHx8fIyMjJycnLy8vNzc3Ozs7Pz8/S0tLT09PU1NTV1dXW1tbX19fZ2dna2trb29vc3Nzd3d3e3t7f39/h4eHi4uLj4+Pk5OTl5eXm5ubn5+fo6Ojq6urr6+vs7Ozt7e3u7u7v7+/w8PDx8fH09PT19fX29vb39/f4+Pj5+fn6+vr7+/v8/Pz9/f3+/v7/xx////8KXFhiAAAAAWJLR0RnW9PpswAAAvtJREFUeNrt3GtXElEUBuDpZlAqylVHCyMwyi4SpkaWAl4qk8wwUhJlmOn9//+gLzbKbc6AAm5991p82WvPOedZZw6zYM3aGupCQ4tYkZDSsKDrocNVvz8jHQKYY1ZiT/6OAJllRPSpsnzIqIG9SjYmHpKfAzI4CIuHjJeBtyHfZwCatiI1/m+BYV2Dw35NniOEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQUp/6Wx+DgbRbxOAhEb/fXJ98akiH1AIAHtSWlqRDSvdDGcOHnYR0SPV7zVsaQyHeO0jTJapEV5C9bUz9fIjsvHRIefhRxHqpjxzA3ftajXOudHGJOtHV+1rV04/wHenDc2QQkFbLJISQwUDaP90IIYQQQgghpAvIxRO9gFxG4lZ9XBFIx6sihJD+QVRjEEIIIYQQcoMgahkhhBDSe0iLX3ydQdSLIKR9ghCJkIYEIYQQIhPS8d+AhPQboppFw9HMxBrs/lqCIbObpgezP67YjnQ8iwagHDzrryUZUgsUz/prCYZY05vn+msJhiwM6XrR7q/V9OJU4xQuEpcxRsezNPbXEn3Yzz9Hulg3IYT0C3LxxI2CNCQIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQlpA7P5a0iF2fy3hkLP+WsIhVbu/lvRby+6vJR1i99eSG6ffu1XHxlRNcfWq3I0iIK4tJK2nXVyV0lOqEmvjGWAtvnCu+jMzkQWMiOlYdRSd3MAH/UnVPWR/ApFfSkcpiOBvRc3XtAeYW1ZUJbZMD5C8azhWLX4xvZVR692Se0huHm9ySogxkhs3lFVewPM4WlFUlUMoJEYUox1+jAFIbLuHrKaQWlMu8TicDp+4gdw7/qS4t2qBohk4UUF2nieBfLyDM/ItgXhBucT113i14QbixW7M+SRNb6EQ0u8kHavyZQxj2/kgNUCsYDRoqXfEF/Mdu4G8D43uOtakh3R9H1DsyKZvOmneDjt+D/0DTzolrPMHmggAAAAASUVORK5CYII=", + "deprecated": true, + "image": "tb-image:dGltZXNlcmllc19iYXJfY2hhcnRfc3lzdGVtX3dpZGdldF9pbWFnZS5wbmc=:IlRpbWVzZXJpZXMgQmFyIENoYXJ0IiBzeXN0ZW0gd2lkZ2V0IGltYWdl;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAMAAAB+IdObAAABOFBMVEUAAAA3oPR3d3d6enp8fHyBgYGDg4OGhoaNjY2RkZGSkpKTk5OUlJSVlZWWlpaXl5eYmJiZmZmampqcnJydnZ2enp6goKChoaGioqKjo6OkpKSlpaWmpqanp6eoqKipqamqqqqrq6usrKytra2urq6wsLCxsbGysrK0tLS1tbW2tra3t7e4uLi5ubm6urq8vLy9vb2+vr6/v7/AwMDBwcHDw8PExMTHx8fIyMjJycnLy8vNzc3Ozs7Pz8/S0tLT09PU1NTV1dXW1tbX19fZ2dna2trb29vc3Nzd3d3e3t7f39/h4eHi4uLj4+Pk5OTl5eXm5ubn5+fo6Ojq6urr6+vs7Ozt7e3u7u7v7+/w8PDx8fH09PT19fX29vb39/f4+Pj5+fn6+vr7+/v8/Pz9/f3+/v7/xx////8KXFhiAAAAAWJLR0RnW9PpswAAAvtJREFUeNrt3GtXElEUBuDpZlAqylVHCyMwyi4SpkaWAl4qk8wwUhJlmOn9//+gLzbKbc6AAm5991p82WvPOedZZw6zYM3aGupCQ4tYkZDSsKDrocNVvz8jHQKYY1ZiT/6OAJllRPSpsnzIqIG9SjYmHpKfAzI4CIuHjJeBtyHfZwCatiI1/m+BYV2Dw35NniOEEEIIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQUp/6Wx+DgbRbxOAhEb/fXJ98akiH1AIAHtSWlqRDSvdDGcOHnYR0SPV7zVsaQyHeO0jTJapEV5C9bUz9fIjsvHRIefhRxHqpjxzA3ftajXOudHGJOtHV+1rV04/wHenDc2QQkFbLJISQwUDaP90IIYQQQgghpAvIxRO9gFxG4lZ9XBFIx6sihJD+QVRjEEIIIYQQcoMgahkhhBDSe0iLX3ydQdSLIKR9ghCJkIYEIYQQIhPS8d+AhPQboppFw9HMxBrs/lqCIbObpgezP67YjnQ8iwagHDzrryUZUgsUz/prCYZY05vn+msJhiwM6XrR7q/V9OJU4xQuEpcxRsezNPbXEn3Yzz9Hulg3IYT0C3LxxI2CNCQIIYQQQgghhBBCCCGEEEIIIYQQQgghhBBCCCGEEEIIIYQQQlpA7P5a0iF2fy3hkLP+WsIhVbu/lvRby+6vJR1i99eSG6ffu1XHxlRNcfWq3I0iIK4tJK2nXVyV0lOqEmvjGWAtvnCu+jMzkQWMiOlYdRSd3MAH/UnVPWR/ApFfSkcpiOBvRc3XtAeYW1ZUJbZMD5C8azhWLX4xvZVR692Se0huHm9ySogxkhs3lFVewPM4WlFUlUMoJEYUox1+jAFIbLuHrKaQWlMu8TicDp+4gdw7/qS4t2qBohk4UUF2nieBfLyDM/ItgXhBucT113i14QbixW7M+SRNb6EQ0u8kHavyZQxj2/kgNUCsYDRoqXfEF/Mdu4G8D43uOtakh3R9H1DsyKZvOmneDjt+D/0DTzolrPMHmggAAAAASUVORK5CYII=", "description": "Displays changes to time-series data over time—for example, daily water consumption for the last month.", "descriptor": { "type": "timeseries", @@ -21,6 +21,5 @@ "basicModeDirective": "tb-flot-basic-config", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":false,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < 0) {\\n\\tvalue = 0;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"timewindow\":{\"realtime\":{\"timewindowMs\":60000},\"aggregation\":{\"limit\":200,\"type\":\"AVG\"}},\"showTitle\":true,\"backgroundColor\":\"#fff\",\"color\":\"rgba(0, 0, 0, 0.87)\",\"padding\":\"8px\",\"settings\":{\"stack\":true,\"fontSize\":10,\"fontColor\":\"#545454\",\"showTooltip\":true,\"tooltipIndividual\":false,\"tooltipCumulative\":false,\"hideZeros\":false,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"defaultBarWidth\":600,\"barAlignment\":\"left\",\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false},\"title\":\"Timeseries Bar Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"widgetStyle\":{},\"useDashboardTimewindow\":true,\"showLegend\":true,\"actions\":{},\"configMode\":\"basic\",\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\"}" }, - "externalId": null, "tags": null } \ No newline at end of file diff --git a/application/src/main/data/json/system/widget_types/timeseries_line_chart.json b/application/src/main/data/json/system/widget_types/timeseries_line_chart.json index e83b213ae0..87f7330657 100644 --- a/application/src/main/data/json/system/widget_types/timeseries_line_chart.json +++ b/application/src/main/data/json/system/widget_types/timeseries_line_chart.json @@ -1,8 +1,8 @@ { "fqn": "charts.basic_timeseries", "name": "Timeseries Line Chart", - "deprecated": false, - "image": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAXk0lEQVR42u2deXwb1bXH+Y+lfY/utK98WkogZSk7LcujBF4pAT6lLaWvlBZeCrTw4AGFvrbQkPYRwlIWfyCr7WA7XrN4wzZx7DjxIu+2vEteZHmTJdmOd8faR9K838y1FUWWZVmaGQM585mPPmNZR/fOvV+de+455849iw/7OOuss/gojpaWFhI8cwQFVlQqVbp4JCUlzc3NaTSaveKhVqsJLBKMHCx2eDyeuLg4r9ebl5c3NjZGGosEpQEL+qm+vh4XKSkp0F4ZGRkWi4XAIsFowdqzZw/HcbiAurLb7Z2dndnZ2QQWCUYFVnd3d1FRkU914XV2djY5OZnAIsGowIqPj/cNfOXl5bDiY2Nj+/v7/anC0UIHHeEdwZUQxkRY8WS8k6AENhb5sUiQwCJBAosancAisEiQwCJBAosancAisEiQwCJBAosancAisEiQwCJBAosancAisEiQwCJBAosECSwCiwQJLBIksEiQwCKwSJDAIkECiwQJLAKLBAksEiSwSJDAIrBIkMAiQQKLBAksAosECSwSJLBIkMAisEiQwCJBAuuzK5jV7SKwCCyJBd+pd35nz7x6xE1gEViSCb7XIFCF84lCO4FFYEkj+IFaoOqSuPn1cfMX75nvnvQQWARWtIKvFg6CqnWx8+83Op8ptuP6hWN2AovAikowsd3FqIppcDaOuIv7uUtj5y+JnR+a9RBYBFaEgvs6BKow9r1V6wBV7PzdYRve3KJyEFgEViSC6VrXxSJVL+YN+6jC+ZHOBQV2Wfz8uNW75lXVTni2q50/zbLWGN0E1qdAEP4q0AOqXq92ZKi6/cHC+auPBKX1dp1zTarq9vBweWytctyaamETVZy3pFim7V6FwMKOX2yPQmwup9Pp0tLScnJy2J5NBFaII7uHA1Xora3Vwgi4FKz9ncIQeXWC5aTTq1hVwU1OD/fMUfv3PjzF0w1JlieL7HcftOIaF0qA5Xa7ExISfG/t2LED79SJB4EV4ijs4+BWQD+9orIzjJaChfMnWUJfxrY45a6qYc6T1O56pMB2ady8j6cfpllfOOZI1bgaxMpgSnGVSNtbRf2ygzU9Pf3mm29iYybs+wUthe2Z8KbRaMSOmATWcgd6iFH1t3K7j6GgYCWIs8Ub91nsnPRVbWpu0Yx7Pmh03i/iy851cfP3HLS+WuU40s8trc/OJsHTtj52ric8H1vkYLlcLrPZDC21fft2QIZ9VvGmyWTKzc0lsIIeRwc4phX+Umb377OgYOH80X5BSaRpXBJW1eXhXy53XL131scTBtxNH9t2qJ2Vw1zQavjOxw8LPrb7DlkdbjnBws6XBoMBF9hVdXx8fOfOnbjGRpilpaUE1tKjwuD+brzQkc+V2AM6bDmwdoju+NvTrW6PZFVl4UicN6dY4YyFF63O5A7Nk+8Eed9PnIHstmqHjGBhp8Jdu3YdOHAAJjx2kysuLsbWvbt378ZemP5U0X6FOJLKdevjTqJLHs0cAUZhnukV3awjPyjRS1KNA6qudXtOrgMZhYPhV8P/fLe4/5LYk5jMJpTp5N2vEANi0GvSWL4DHqDL9wpKAhoiqCZYTmPhfLvOAcG7D1g93miranV578wQLKo/lTpClBj6hOAWlTAg3rDPsqKbjfxYMgo2jbqvFOdTyFloWL63lutIjFPf3yeIlw5xUVZ1c4XA6J0ZllqzOxqw6s3un4omPywzL4G1JoLNY27mE3rssB390bh6sHD+X6UAxC9ybNFUtXLYjfELqRPZ3dyKJYYGC6/HBrlrE4X7golGYCkniN9xy5h7W42DUYXYXwiqVuxmmMyYuOF7GszuyKoKz+dNycI3/KPSEU6J4VR1T4sTpGI60jnhIbBkF+ydEpxDd2Sccg49nG+rN4XbW8udfy1ziGrPFllVnz0qWEX3HrL6+I4eLJxPiRk+Pz5gtXEEljyCjKd/3zd9KhKyzwJT3ee5jhKsMgN3+V5B5cCrudqq5uo4CF7xoQUe//BLDKeqNSb3hnSLtIkYBJZwwAeNJKr/2G/1j6yBpzRtWDytqpuZ1nm+xL6qqo7Me68RjaF36hyrLTGcqsJiWy+6fIsHuGhatc7sZhH3Mxos/bQHmcQYAnw8wZLFuPDGkYHQtlQ0YBUtJgAitBdmVWHq/bZAyJJArkQEJYZZ1deqhWH6+iTLqCWSePngrOepIjtrRrTemQWW082PznvbTgjxsnsOWf2DIU8ese9rdzGeJOytEBGVVyocYd5jkhhtBPTHBzn5wIJu/kWugO9v8m2nnG1el3CGbNVZh/f1GgdTeBjoETxFVFResMb82JcbLLTFpM0LIwnauEDPJXe4/ppvQOfhZ/TLXBuGuasSTuWQsBNx/scL7QltrgCrXG6wPupdSAA8seiZDHGPUKvMK4vpW8QlhikIcK9LspyWi9H7DF/1Rb7jXn74XX6+GaD5V5Xz8Cka1/WiCO4IjYkoaqO46E1esKC64VeUFawdTc6NB63wPbK8qNAnxiAYT3ekWx4rtMe3LhtckxssnL/OE3TDPxcTAJe7R/QcS1tAnylT1Q/bhGxYhNih13nj+7zq7NPO2gtnau/jRxJ4x3DZEHfXolWK1KDM7lPaVF6wgD8zC+QDyzzvZfFgdkIbIyKLjJEHc23wJm/KHEHqyHv1Tnj/cns4mKVhWk4KgHWgU2gcuMfmHN4Q9xgjLlG8KcW6XMKCHFV9tkQYqV/K+YivPI9XncOP7uPHMviex/m6i/wh6z16TVLu83/Oyo1tnAr4BhnBwsDkM2KqZUuy/psY3IBNUAJoTGvAx3KC7QbdaMfLjSOh0ldYOGVXk3O5e2wdcyPlC5oYzg4lfwPwPjyRqZ4v+4oAkOGN0zy009pDha+XHb7fWv4lH2Heys+fbLzTpHmta6BGbXbICxZSY9m46wtiSA4WZlXQ2Ehqy+91rZXiWU5wuvnXaPHxtqcbR5ZlImkxARBuyaX3iDeZ++OPxxwKK9cW45i1ej3qX1DwcIF+wXJH5hYML2anAvfnik826yrN2q3zDbd7Kz/ng8xd9dWZpgd554gsYCEHbUO60Chv1DpYKAqRV8nB+suCF9u+hiNaUMHOgXphBBEberLlUfYjDnqyDHQopKX3+HcxsIjE4hqToqO22mw/qb4LNR+tvPmKuBOYLxtPemDVsA7FeVfKBOwKf5Em01xP37GRjs2W+ptx457K83mPTRawEtuE3yKcubBpYOWwZEUk0UoIFrwm+N1cFj+tbXt/qvm3zaaZTwxYHH7E6Jjp5l+5q74iXjykHrEF/fwuMUv4tjSLurk1aKQ5q4dT2BycaP096uysubh12PSQOMO4cnFFBiY90LKhS2wbNuj6imUZCi0u741ifgimXWzAZn/uOq6XECwsXX8k5bC5/FqmGGz117cZBj4JYOl7s1AfV/WFYL1zoJGr/gb+nG26v8k8v/TzcB1BJ6Fx3i/p890azHm2WmuLyq7wPGO4813U1lP5hc6BBvxZbuBYqg/0FhRE+E4+WcBiE5n7Mq2+Yt6sFZTWbUlTbq80YA2NaisL72VI2WuvstddLfRlzbfRkWsL1gGVxl57JSoz2B3P3tEONnPV38Q7c+q7m0yzS0XeqRca5/bkKV/bPF8SGGlWBqxefQGvOhdjmb43x/cmVnj/6bjDf066NmDBRckyTDK0tq6BamPnWzp9Yb3RCS8A3sQqvGjB4qZ4/YselWAtWisuMHR9AJug2TQ517hR/Kl90b9RlAerSfV3QX3WXeNvV2mGNK5qYaKOIbLZOLVcAiBSo3B/Bb0c81/DalbSM6IZbGcDt0n7RvQlSg9WjKpn84Hddccecldd4JspQJc01Lx8R0IH1L7LEylYXk7wy9UKv35Pxbl5BY+W6Uf8TE7HifbnxOLOMWtfWxOwwLdDJdx1r/7jgH+1G/odtZcJP4b6H2DOFZgAKJqhD+TYEKi4TpzrvF3nUGwCK04Dxx11l6N6Uy0Ph3aRKAuWtZs3x/KdD3lqvuHvQLPVXTve+hSGqgVvh+qc+iN31jan8B7rqsGaOsqrr2Hf01V+z8bExv85GsT+MHa+Iyrzs1EuNJnCYGFahKIxpVrGqh1ko6St/rrWYXNAAuCV8XPChEv0LyAAFV6JLoyz0d+j2mydb9ggKNTG25rMFkkaJwqwnKP8eLYQSKq/xB+mE6XfUpc+NNi9t214yFdM56AaHh1nxaJLrfqrfM9j/HSpGLZf6bD1CjEEJth45XBfFkt3LB4I/sPS9+ZiQBRsmsaNWapGxcDCbEgs95wAO8//bB0esdbdgLo5ar/bbujz/9cT2WZfOLxkgFsRqT5dJgZcfNWM6qqBnlS12RnxPeJHKM42LmobHpaqcVYP1vghvvdpvvHy0+JHdd/muzeN6JM2JHRihlzUH7xdDqmaYvLjmotuOyWovlaIRrnGg5fHTfN9/8tXfl74ZM3XhE96nQiZregzxIwGg6/AlurydoNeGbAmWzYJNrvqgdAfazGesNbfJMznay9tH+o9FaEr07EEQKxADCEO7YJpAbhcdHl/zjc49OvSQzjMlrtHUcef7an6knawRcLGWT1Yi34/vvrL/hFv/PcRMWEoRJejQvEtwoTxgfQ218Brp1QdAlL4Kug/X3rGgjl1ofiBc6dqf8Y7T+DtjnHPxaJhuzR7ZIn+GGb9h9l+d79KbrDQK6gnHIN5qrKVTTHjFHN0uWq+BZPZVyIidI8V2kMitRfuJdZoMInwJzyTtap3HbVXLLxZu97QtT2c4YyVqNMf8YrRwD7dIWkbZ/Vg9T7LG2NEmE4zwrHgjoVUkXobukIbDy4GyEDP5Me89kHeFxCA5ht4hR/P4tXXLbzTvpG3dPhmhYgrC4vpjjvCM6VnzKofiVPF8/t0+2UFC64EwVXd8XKYgvA7zKl/LHL/TaYqIIiEk4pgrddsmsbkmvksBPO/7kaMg75IEQRhTQ707LPXfo99APAZunYEdZv5n4WqfE/Vl/F5fLnkjSON8Q59hRmN/0NXQlQI4Qt8Elm2M45FA8s5xpt28k03nja8Qp+NpfvPCptH3cwLHILdwEcIqbRj7S+yqSIs6xXnO5GBhTmggEjV11tM4+ELQgMJMTUhuHZB10BtUEFM1jDD5aoWpkSYUYpIcctU1QVHlBhUOZshC1lMVJcbkedV68Rp4G/CbxalwTrcx7Ek8SpjWBVij/WBHzXwi+bqeN1TfP13+OH3eE9gdiVSGCCFVS6rbQJD105R4SNstwndKS1YsJph3+DLMQatlkjEeRDtEdn6yjFVeoCZD/3HNAqbaSIYF15VuV59nqXh1gW8qr4OvEB8ANNsLJ5v+GGY4+YagIWnXLBJsv+zN0NXCKlIF4u6ZzK89bcAC4+lY0NtuYGLoAkQvXJXf401ZatxVEKwYOiIxs1lDNnVjqHgcqL1CYEA1fmoJPNKQMuyia1Y4Q09faURVLWrv2qm6efsSwAovrPVaFycBj6JNy2qi4CvTDMbCcDC4zfR5bemWle15u5BMbcaidJhgsXyLf2fRLXaJtAOtTlr1jGzt8PQJQlYMJXY9FMcoSJ2gLngixGneP8K/6RX9S9syjLd/J9w00TZzd39lbNNP1nAq/ILY20vjHS8wlCDjSWfLyZasJAzjwdaosuxNmFVFcJiI5b0jaUNK5abVNbDHDwrPvYpdBMgXG9puIUNEJGpgYATowzTgj4zJVJ3JdetenzRg3DeZMt/aQY7JOxmuNamm355akavOrdXny+r9zhasFjWx10HLBEs5nw4P9xHVW9MnQgz1L9iEzSZT0ITMA8Qpk7RtB0whftHiAQMVEsSYDFrXp1o/QNST2WawAJWIAtw4buSO94VFVhY9MOS+LBqKoIKIe0TyZ9IAfVfXrf0QMY+m0WuVl0t3wQucTgQfr4TrY8HtV7Dabvxtv9mSVefkJzVsCMEQwqUGBVYsJDE0Kk14gptEp+vj0TQEIX+PFv4DMK00jYBxgIk0bI5fASJXJohLXQecr07DD2fLrCUEYwcLCyWxWI3TO4OdkXuHCrsc7E1wVg6F7RE+AwF62rvbLVR+iaA15tlHHDV/xZgcq3YdkjcgyCmWp9tPtYArD+XCjE7hHGirBDWH7OHeQb1u94nrvN5/iOjTE0AB8+s+j5mchk73w5TsKevjDmflnouCKyowOqbEfLNcX4cXjJaiAohSQGpCtB8Sx/OVCj6XZHWnFLRI18TwJMkprucwzyoLBISUtDFvNv+IBJY0oD1e1HNPF1sl6RCbHnkH47Ygy5LRFqzAm3X13uQTfEQuoaLMoQgclRYPA4TTAJLSrCwipKlGBwblCbGhJVhLGMEz87zlZUvZuj+IDnax2yuIkNhqI2loyDKdlyVFtwjarY4a4SkDOSonAl8KArWw3mrjtmtWCEkLDCLbSFM5F0IE2GhgZJth6DvnPpeMdn1vKAjHRIBWH7BcstQCawIwTomPo4BTnCVgZOwQogAsg1eak1CnbLFVdS3pCyEiZRsu0WTS1xr2vo7/+QTZASwgONy8WACK3Kw2DSN7XQlbYVeLrezdG9ucRU126d0TdquWvU+CwMLQethE3vzRNsfhVTgpp+dOXwoB5Zo96yw6DviJwqzRyE+J9ryt6VJ/PzW1Qoi/46ZXEgGR74U0ogRHkY8JHQUj8A6DSybzZaZmZmamqrVavGnRqNhexdiM7AAsGC2v9fglKlC/xCfVsDOD/xSv9eq7bBOiz3FAHkHWGnNFv+cUXxEC1Z+fr5er8cWmDExMezP0dHRoBoL1k9kD+cMp0Jwr98gLt3ckGHxL2UN2w7LE3wmF1JNfMMigbWKoRBbNSUmJvLibqvYpAk7geGdALAiq0r4FXpdfL7qrtOTcNa87fp1GTC5TJqtZxofEoDFNlmdmJjgxV3m7HY7tpXLzs5WGCysN0dOX8Mnr+2Qcxf0yQsEViiwsJXc/v37MRoygJhphT3lkpOTFQaLsUW99RkBS6VSbdu2LV08oLTKy8uxjS828O3v7/enCkdkW+PReaadp+1XeNoiZI6DGltqvCugsUjwTPRjEVgkSGCRIIFFggQWgUWCBBYJElgkSGBR25EggUWCBBYJEljUdiRIYJEggUWCBBY1OgkSWCRIYJEggUWNToIEFgkSWCRIYFGjkyCBRYIEFgkSWNToJEhgkSCBRYIEFjU6CRJYJEhgkSCBRY1OggQWCRJYJEhgUaOTIIFFggQWCRJY1OgkSGCRIIFFggQWNToJElgkSGCRIIFFjU5gEVgkqBRYOp0uLS0tJycH+1MQWCQoGVg7duzAZmB14kFgkaA0YEFLYXsmXBiNxry8PAKLBKUBy+VyxcXF4cJkMuXm5hJYJCjZULhz5068YiPM0tJSAosEJQOruLgYGxfu3r0be2EG7FdIBx3hHkE9CxgQQzgdeGUPKvHTWKLS90PHGXIQWHR8AsCqqKiA+YVXxepXVlaGEvEqd0HYqri3t7egoMD3Z3V19ZEjR+Qr0Wq1ZmZmpqamYp7k8/WkpKR4PB6ZSrTZbIcOHYL3G3eKP5ubm9G2WVlZISwfJcCanJxEK+ACNz81NaUAVdPT04mJibjA68zMjKxlDQ4O4gcTExPD/iwqKqqvr5e1RLgJ+/r6gJGv0MOHD2/ZsiUg4CHhgZ/KwMAASty+fbvdbt+zZw9+P1VVVQGecKXB6urqYg6I48eP41oBsNDEmJyirPj4ePma2/9Ai7MLdPbBgwfx+0YHyFqixWJJSkpijkOghvuV9U7n5uZaW1uzs7P9+TYYDGsJlkajYUMSXrVarQLd7HA4kpOToUjw6nQ6lQRr69atKL2jo0PW0RChs4SEhImJCWgRXOAe5QYLAZXCwkKoRvZnd3d3gBt8DcAaHh5mQR5UBT8vBboZ4fCSkhJcHD16lJkFioHFLkZGRvx/3JJbdfv378doyNQV9BYsns2bN/t6XfIDGEFB4mLXrl14haKSz6Q7a1UNAVsHowNeca2MxkLgEl2LV1wrCVZDQwN6GlYI2JKpLGjibdu2pYsH7Ff2pqwaS6/XoyVBM9gFTy+99BKGAplmY/8Pl7O7ukBGoYYAAAAASUVORK5CYII=", + "deprecated": true, + "image": "tb-image:Z2F0ZXdheV9nZW5lcmFsX2NoYXJ0X3N0YXRpc3RpY3Nfc3lzdGVtX3dpZGdldF9pbWFnZS5wbmc=:IkdhdGV3YXkgZ2VuZXJhbCBjaGFydCBzdGF0aXN0aWNzIiBzeXN0ZW0gd2lkZ2V0IGltYWdl;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAACgCAIAAADGnbT+AAAABmJLR0QA/wD/AP+gvaeTAAAXk0lEQVR42u2deXwb1bXH+Y+lfY/utK98WkogZSk7LcujBF4pAT6lLaWvlBZeCrTw4AGFvrbQkPYRwlIWfyCr7WA7XrN4wzZx7DjxIu+2vEteZHmTJdmOd8faR9K838y1FUWWZVmaGQM585mPPmNZR/fOvV+de+455849iw/7OOuss/gojpaWFhI8cwQFVlQqVbp4JCUlzc3NaTSaveKhVqsJLBKMHCx2eDyeuLg4r9ebl5c3NjZGGosEpQEL+qm+vh4XKSkp0F4ZGRkWi4XAIsFowdqzZw/HcbiAurLb7Z2dndnZ2QQWCUYFVnd3d1FRkU914XV2djY5OZnAIsGowIqPj/cNfOXl5bDiY2Nj+/v7/anC0UIHHeEdwZUQxkRY8WS8k6AENhb5sUiQwCJBAosancAisEiQwCJBAosancAisEiQwCJBAosancAisEiQwCJBAosancAisEiQwCJBAosECSwCiwQJLBIksEiQwCKwSJDAIkECiwQJLAKLBAksEiSwSJDAIrBIkMAiQQKLBAksAosECSwSJLBIkMAisEiQwCJBAuuzK5jV7SKwCCyJBd+pd35nz7x6xE1gEViSCb7XIFCF84lCO4FFYEkj+IFaoOqSuPn1cfMX75nvnvQQWARWtIKvFg6CqnWx8+83Op8ptuP6hWN2AovAikowsd3FqIppcDaOuIv7uUtj5y+JnR+a9RBYBFaEgvs6BKow9r1V6wBV7PzdYRve3KJyEFgEViSC6VrXxSJVL+YN+6jC+ZHOBQV2Wfz8uNW75lXVTni2q50/zbLWGN0E1qdAEP4q0AOqXq92ZKi6/cHC+auPBKX1dp1zTarq9vBweWytctyaamETVZy3pFim7V6FwMKOX2yPQmwup9Pp0tLScnJy2J5NBFaII7uHA1Xora3Vwgi4FKz9ncIQeXWC5aTTq1hVwU1OD/fMUfv3PjzF0w1JlieL7HcftOIaF0qA5Xa7ExISfG/t2LED79SJB4EV4ijs4+BWQD+9orIzjJaChfMnWUJfxrY45a6qYc6T1O56pMB2ady8j6cfpllfOOZI1bgaxMpgSnGVSNtbRf2ygzU9Pf3mm29iYybs+wUthe2Z8KbRaMSOmATWcgd6iFH1t3K7j6GgYCWIs8Ub91nsnPRVbWpu0Yx7Pmh03i/iy851cfP3HLS+WuU40s8trc/OJsHTtj52ric8H1vkYLlcLrPZDC21fft2QIZ9VvGmyWTKzc0lsIIeRwc4phX+Umb377OgYOH80X5BSaRpXBJW1eXhXy53XL131scTBtxNH9t2qJ2Vw1zQavjOxw8LPrb7DlkdbjnBws6XBoMBF9hVdXx8fOfOnbjGRpilpaUE1tKjwuD+brzQkc+V2AM6bDmwdoju+NvTrW6PZFVl4UicN6dY4YyFF63O5A7Nk+8Eed9PnIHstmqHjGBhp8Jdu3YdOHAAJjx2kysuLsbWvbt378ZemP5U0X6FOJLKdevjTqJLHs0cAUZhnukV3awjPyjRS1KNA6qudXtOrgMZhYPhV8P/fLe4/5LYk5jMJpTp5N2vEANi0GvSWL4DHqDL9wpKAhoiqCZYTmPhfLvOAcG7D1g93miranV578wQLKo/lTpClBj6hOAWlTAg3rDPsqKbjfxYMgo2jbqvFOdTyFloWL63lutIjFPf3yeIlw5xUVZ1c4XA6J0ZllqzOxqw6s3un4omPywzL4G1JoLNY27mE3rssB390bh6sHD+X6UAxC9ybNFUtXLYjfELqRPZ3dyKJYYGC6/HBrlrE4X7golGYCkniN9xy5h7W42DUYXYXwiqVuxmmMyYuOF7GszuyKoKz+dNycI3/KPSEU6J4VR1T4sTpGI60jnhIbBkF+ydEpxDd2Sccg49nG+rN4XbW8udfy1ziGrPFllVnz0qWEX3HrL6+I4eLJxPiRk+Pz5gtXEEljyCjKd/3zd9KhKyzwJT3ee5jhKsMgN3+V5B5cCrudqq5uo4CF7xoQUe//BLDKeqNSb3hnSLtIkYBJZwwAeNJKr/2G/1j6yBpzRtWDytqpuZ1nm+xL6qqo7Me68RjaF36hyrLTGcqsJiWy+6fIsHuGhatc7sZhH3Mxos/bQHmcQYAnw8wZLFuPDGkYHQtlQ0YBUtJgAitBdmVWHq/bZAyJJArkQEJYZZ1deqhWH6+iTLqCWSePngrOepIjtrRrTemQWW082PznvbTgjxsnsOWf2DIU8ese9rdzGeJOytEBGVVyocYd5jkhhtBPTHBzn5wIJu/kWugO9v8m2nnG1el3CGbNVZh/f1GgdTeBjoETxFVFResMb82JcbLLTFpM0LIwnauEDPJXe4/ppvQOfhZ/TLXBuGuasSTuWQsBNx/scL7QltrgCrXG6wPupdSAA8seiZDHGPUKvMK4vpW8QlhikIcK9LspyWi9H7DF/1Rb7jXn74XX6+GaD5V5Xz8Cka1/WiCO4IjYkoaqO46E1esKC64VeUFawdTc6NB63wPbK8qNAnxiAYT3ekWx4rtMe3LhtckxssnL/OE3TDPxcTAJe7R/QcS1tAnylT1Q/bhGxYhNih13nj+7zq7NPO2gtnau/jRxJ4x3DZEHfXolWK1KDM7lPaVF6wgD8zC+QDyzzvZfFgdkIbIyKLjJEHc23wJm/KHEHqyHv1Tnj/cns4mKVhWk4KgHWgU2gcuMfmHN4Q9xgjLlG8KcW6XMKCHFV9tkQYqV/K+YivPI9XncOP7uPHMviex/m6i/wh6z16TVLu83/Oyo1tnAr4BhnBwsDkM2KqZUuy/psY3IBNUAJoTGvAx3KC7QbdaMfLjSOh0ldYOGVXk3O5e2wdcyPlC5oYzg4lfwPwPjyRqZ4v+4oAkOGN0zy009pDha+XHb7fWv4lH2Heys+fbLzTpHmta6BGbXbICxZSY9m46wtiSA4WZlXQ2Ehqy+91rZXiWU5wuvnXaPHxtqcbR5ZlImkxARBuyaX3iDeZ++OPxxwKK9cW45i1ej3qX1DwcIF+wXJH5hYML2anAvfnik826yrN2q3zDbd7Kz/ng8xd9dWZpgd554gsYCEHbUO60Chv1DpYKAqRV8nB+suCF9u+hiNaUMHOgXphBBEberLlUfYjDnqyDHQopKX3+HcxsIjE4hqToqO22mw/qb4LNR+tvPmKuBOYLxtPemDVsA7FeVfKBOwKf5Em01xP37GRjs2W+ptx457K83mPTRawEtuE3yKcubBpYOWwZEUk0UoIFrwm+N1cFj+tbXt/qvm3zaaZTwxYHH7E6Jjp5l+5q74iXjykHrEF/fwuMUv4tjSLurk1aKQ5q4dT2BycaP096uysubh12PSQOMO4cnFFBiY90LKhS2wbNuj6imUZCi0u741ifgimXWzAZn/uOq6XECwsXX8k5bC5/FqmGGz117cZBj4JYOl7s1AfV/WFYL1zoJGr/gb+nG26v8k8v/TzcB1BJ6Fx3i/p890azHm2WmuLyq7wPGO4813U1lP5hc6BBvxZbuBYqg/0FhRE+E4+WcBiE5n7Mq2+Yt6sFZTWbUlTbq80YA2NaisL72VI2WuvstddLfRlzbfRkWsL1gGVxl57JSoz2B3P3tEONnPV38Q7c+q7m0yzS0XeqRca5/bkKV/bPF8SGGlWBqxefQGvOhdjmb43x/cmVnj/6bjDf066NmDBRckyTDK0tq6BamPnWzp9Yb3RCS8A3sQqvGjB4qZ4/YselWAtWisuMHR9AJug2TQ517hR/Kl90b9RlAerSfV3QX3WXeNvV2mGNK5qYaKOIbLZOLVcAiBSo3B/Bb0c81/DalbSM6IZbGcDt0n7RvQlSg9WjKpn84Hddccecldd4JspQJc01Lx8R0IH1L7LEylYXk7wy9UKv35Pxbl5BY+W6Uf8TE7HifbnxOLOMWtfWxOwwLdDJdx1r/7jgH+1G/odtZcJP4b6H2DOFZgAKJqhD+TYEKi4TpzrvF3nUGwCK04Dxx11l6N6Uy0Ph3aRKAuWtZs3x/KdD3lqvuHvQLPVXTve+hSGqgVvh+qc+iN31jan8B7rqsGaOsqrr2Hf01V+z8bExv85GsT+MHa+Iyrzs1EuNJnCYGFahKIxpVrGqh1ko6St/rrWYXNAAuCV8XPChEv0LyAAFV6JLoyz0d+j2mydb9ggKNTG25rMFkkaJwqwnKP8eLYQSKq/xB+mE6XfUpc+NNi9t214yFdM56AaHh1nxaJLrfqrfM9j/HSpGLZf6bD1CjEEJth45XBfFkt3LB4I/sPS9+ZiQBRsmsaNWapGxcDCbEgs95wAO8//bB0esdbdgLo5ar/bbujz/9cT2WZfOLxkgFsRqT5dJgZcfNWM6qqBnlS12RnxPeJHKM42LmobHpaqcVYP1vghvvdpvvHy0+JHdd/muzeN6JM2JHRihlzUH7xdDqmaYvLjmotuOyWovlaIRrnGg5fHTfN9/8tXfl74ZM3XhE96nQiZregzxIwGg6/AlurydoNeGbAmWzYJNrvqgdAfazGesNbfJMznay9tH+o9FaEr07EEQKxADCEO7YJpAbhcdHl/zjc49OvSQzjMlrtHUcef7an6knawRcLGWT1Yi34/vvrL/hFv/PcRMWEoRJejQvEtwoTxgfQ218Brp1QdAlL4Kug/X3rGgjl1ofiBc6dqf8Y7T+DtjnHPxaJhuzR7ZIn+GGb9h9l+d79KbrDQK6gnHIN5qrKVTTHjFHN0uWq+BZPZVyIidI8V2kMitRfuJdZoMInwJzyTtap3HbVXLLxZu97QtT2c4YyVqNMf8YrRwD7dIWkbZ/Vg9T7LG2NEmE4zwrHgjoVUkXobukIbDy4GyEDP5Me89kHeFxCA5ht4hR/P4tXXLbzTvpG3dPhmhYgrC4vpjjvCM6VnzKofiVPF8/t0+2UFC64EwVXd8XKYgvA7zKl/LHL/TaYqIIiEk4pgrddsmsbkmvksBPO/7kaMg75IEQRhTQ707LPXfo99APAZunYEdZv5n4WqfE/Vl/F5fLnkjSON8Q59hRmN/0NXQlQI4Qt8Elm2M45FA8s5xpt28k03nja8Qp+NpfvPCptH3cwLHILdwEcIqbRj7S+yqSIs6xXnO5GBhTmggEjV11tM4+ELQgMJMTUhuHZB10BtUEFM1jDD5aoWpkSYUYpIcctU1QVHlBhUOZshC1lMVJcbkedV68Rp4G/CbxalwTrcx7Ek8SpjWBVij/WBHzXwi+bqeN1TfP13+OH3eE9gdiVSGCCFVS6rbQJD105R4SNstwndKS1YsJph3+DLMQatlkjEeRDtEdn6yjFVeoCZD/3HNAqbaSIYF15VuV59nqXh1gW8qr4OvEB8ANNsLJ5v+GGY4+YagIWnXLBJsv+zN0NXCKlIF4u6ZzK89bcAC4+lY0NtuYGLoAkQvXJXf401ZatxVEKwYOiIxs1lDNnVjqHgcqL1CYEA1fmoJPNKQMuyia1Y4Q09faURVLWrv2qm6efsSwAovrPVaFycBj6JNy2qi4CvTDMbCcDC4zfR5bemWle15u5BMbcaidJhgsXyLf2fRLXaJtAOtTlr1jGzt8PQJQlYMJXY9FMcoSJ2gLngixGneP8K/6RX9S9syjLd/J9w00TZzd39lbNNP1nAq/ILY20vjHS8wlCDjSWfLyZasJAzjwdaosuxNmFVFcJiI5b0jaUNK5abVNbDHDwrPvYpdBMgXG9puIUNEJGpgYATowzTgj4zJVJ3JdetenzRg3DeZMt/aQY7JOxmuNamm355akavOrdXny+r9zhasFjWx10HLBEs5nw4P9xHVW9MnQgz1L9iEzSZT0ITMA8Qpk7RtB0whftHiAQMVEsSYDFrXp1o/QNST2WawAJWIAtw4buSO94VFVhY9MOS+LBqKoIKIe0TyZ9IAfVfXrf0QMY+m0WuVl0t3wQucTgQfr4TrY8HtV7Dabvxtv9mSVefkJzVsCMEQwqUGBVYsJDE0Kk14gptEp+vj0TQEIX+PFv4DMK00jYBxgIk0bI5fASJXJohLXQecr07DD2fLrCUEYwcLCyWxWI3TO4OdkXuHCrsc7E1wVg6F7RE+AwF62rvbLVR+iaA15tlHHDV/xZgcq3YdkjcgyCmWp9tPtYArD+XCjE7hHGirBDWH7OHeQb1u94nrvN5/iOjTE0AB8+s+j5mchk73w5TsKevjDmflnouCKyowOqbEfLNcX4cXjJaiAohSQGpCtB8Sx/OVCj6XZHWnFLRI18TwJMkprucwzyoLBISUtDFvNv+IBJY0oD1e1HNPF1sl6RCbHnkH47Ygy5LRFqzAm3X13uQTfEQuoaLMoQgclRYPA4TTAJLSrCwipKlGBwblCbGhJVhLGMEz87zlZUvZuj+IDnax2yuIkNhqI2loyDKdlyVFtwjarY4a4SkDOSonAl8KArWw3mrjtmtWCEkLDCLbSFM5F0IE2GhgZJth6DvnPpeMdn1vKAjHRIBWH7BcstQCawIwTomPo4BTnCVgZOwQogAsg1eak1CnbLFVdS3pCyEiZRsu0WTS1xr2vo7/+QTZASwgONy8WACK3Kw2DSN7XQlbYVeLrezdG9ucRU126d0TdquWvU+CwMLQethE3vzRNsfhVTgpp+dOXwoB5Zo96yw6DviJwqzRyE+J9ryt6VJ/PzW1Qoi/46ZXEgGR74U0ogRHkY8JHQUj8A6DSybzZaZmZmamqrVavGnRqNhexdiM7AAsGC2v9fglKlC/xCfVsDOD/xSv9eq7bBOiz3FAHkHWGnNFv+cUXxEC1Z+fr5er8cWmDExMezP0dHRoBoL1k9kD+cMp0Jwr98gLt3ckGHxL2UN2w7LE3wmF1JNfMMigbWKoRBbNSUmJvLibqvYpAk7geGdALAiq0r4FXpdfL7qrtOTcNa87fp1GTC5TJqtZxofEoDFNlmdmJjgxV3m7HY7tpXLzs5WGCysN0dOX8Mnr+2Qcxf0yQsEViiwsJXc/v37MRoygJhphT3lkpOTFQaLsUW99RkBS6VSbdu2LV08oLTKy8uxjS828O3v7/enCkdkW+PReaadp+1XeNoiZI6DGltqvCugsUjwTPRjEVgkSGCRIIFFggQWgUWCBBYJElgkSGBR25EggUWCBBYJEljUdiRIYJEggUWCBBY1OgkSWCRIYJEggUWNToIEFgkSWCRIYFGjkyCBRYIEFgkSWNToJEhgkSCBRYIEFjU6CRJYJEhgkSCBRY1OggQWCRJYJEhgUaOTIIFFggQWCRJY1OgkSGCRIIFFggQWNToJElgkSGCRIIFFjU5gEVgkqBRYOp0uLS0tJycH+1MQWCQoGVg7duzAZmB14kFgkaA0YEFLYXsmXBiNxry8PAKLBKUBy+VyxcXF4cJkMuXm5hJYJCjZULhz5068YiPM0tJSAosEJQOruLgYGxfu3r0be2EG7FdIBx3hHkE9CxgQQzgdeGUPKvHTWKLS90PHGXIQWHR8AsCqqKiA+YVXxepXVlaGEvEqd0HYqri3t7egoMD3Z3V19ZEjR+Qr0Wq1ZmZmpqamYp7k8/WkpKR4PB6ZSrTZbIcOHYL3G3eKP5ubm9G2WVlZISwfJcCanJxEK+ACNz81NaUAVdPT04mJibjA68zMjKxlDQ4O4gcTExPD/iwqKqqvr5e1RLgJ+/r6gJGv0MOHD2/ZsiUg4CHhgZ/KwMAASty+fbvdbt+zZw9+P1VVVQGecKXB6urqYg6I48eP41oBsNDEmJyirPj4ePma2/9Ai7MLdPbBgwfx+0YHyFqixWJJSkpijkOghvuV9U7n5uZaW1uzs7P9+TYYDGsJlkajYUMSXrVarQLd7HA4kpOToUjw6nQ6lQRr69atKL2jo0PW0RChs4SEhImJCWgRXOAe5QYLAZXCwkKoRvZnd3d3gBt8DcAaHh5mQR5UBT8vBboZ4fCSkhJcHD16lJkFioHFLkZGRvx/3JJbdfv378doyNQV9BYsns2bN/t6XfIDGEFB4mLXrl14haKSz6Q7a1UNAVsHowNeca2MxkLgEl2LV1wrCVZDQwN6GlYI2JKpLGjibdu2pYsH7Ff2pqwaS6/XoyVBM9gFTy+99BKGAplmY/8Pl7O7ukBGoYYAAAAASUVORK5CYII=", "description": "Displays changes to time-series data over time—for example, temperature or humidity readings.", "descriptor": { "type": "timeseries", @@ -22,6 +22,5 @@ "basicModeDirective": "tb-flot-basic-config", "defaultConfig": "{\"datasources\":[{\"type\":\"function\",\"name\":\"function\",\"dataKeys\":[{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"First\",\"color\":\"#2196f3\",\"settings\":{\"showLines\":true,\"fillLines\":true,\"showPoints\":false},\"_hash\":0.8587686344902596,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"},{\"name\":\"f(x)\",\"type\":\"function\",\"label\":\"Second\",\"color\":\"#ffc107\",\"settings\":{\"showLines\":true,\"fillLines\":false,\"showPoints\":false},\"_hash\":0.12775350966079668,\"funcBody\":\"var value = prevValue + Math.random() * 100 - 50;\\nvar multiplier = Math.pow(10, 2 || 0);\\nvar value = Math.round(value * multiplier) / multiplier;\\nif (value < -1000) {\\n\\tvalue = -1000;\\n} else if (value > 1000) {\\n\\tvalue = 1000;\\n}\\nreturn value;\"}]}],\"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,\"grid\":{\"verticalLines\":true,\"horizontalLines\":true,\"outlineWidth\":1,\"color\":\"#545454\",\"backgroundColor\":null,\"tickColor\":\"#DDDDDD\"},\"xaxis\":{\"title\":null,\"showLabels\":true,\"color\":\"#545454\"},\"yaxis\":{\"min\":null,\"max\":null,\"title\":null,\"showLabels\":true,\"color\":\"#545454\",\"tickSize\":null,\"tickDecimals\":0,\"ticksFormatter\":\"\"},\"shadowSize\":4,\"smoothLines\":false,\"comparisonEnabled\":false,\"xaxisSecond\":{\"axisPosition\":\"top\",\"title\":null,\"showLabels\":true},\"showLegend\":true,\"legendConfig\":{\"direction\":\"column\",\"position\":\"bottom\",\"sortDataKeys\":false,\"showMin\":false,\"showMax\":false,\"showAvg\":true,\"showTotal\":false,\"showLatest\":false},\"customLegendEnabled\":false},\"title\":\"Timeseries Line Chart\",\"dropShadow\":true,\"enableFullscreen\":true,\"titleStyle\":{\"fontSize\":\"16px\",\"fontWeight\":400},\"mobileHeight\":null,\"configMode\":\"basic\",\"actions\":{},\"showTitleIcon\":false,\"titleIcon\":\"thermostat\",\"iconColor\":\"#1F6BDD\"}" }, - "externalId": null, "tags": null } \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/controller/AdminController.java b/application/src/main/java/org/thingsboard/server/controller/AdminController.java index 5fae72252f..ebb4bd3916 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AdminController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AdminController.java @@ -32,8 +32,6 @@ import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiParam; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; @@ -52,13 +50,12 @@ import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.MailService; import org.thingsboard.rule.engine.api.SmsService; import org.thingsboard.server.common.data.AdminSettings; -import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.data.FeaturesInfo; import org.thingsboard.server.common.data.FeaturesInfo; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.SystemInfo; import org.thingsboard.server.common.data.UpdateMessage; -import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; @@ -74,8 +71,8 @@ import org.thingsboard.server.common.data.sync.vc.VcUtils; import org.thingsboard.server.dao.audit.AuditLogService; import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.security.auth.oauth2.CookieUtils; import org.thingsboard.server.service.security.auth.jwt.settings.JwtSettingsService; +import org.thingsboard.server.service.security.auth.oauth2.CookieUtils; import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.token.JwtTokenFactory; import org.thingsboard.server.service.security.permission.Operation; @@ -93,7 +90,6 @@ import java.io.IOException; import java.util.List; import java.util.Optional; -import static org.thingsboard.server.controller.ControllerConstants.*; import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_AUTHORITY_PARAGRAPH; import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH; @@ -113,9 +109,7 @@ public class AdminController extends BaseController { private final SmsService smsService; private final AdminSettingsService adminSettingsService; private final SystemSecurityService systemSecurityService; - @Lazy private final JwtSettingsService jwtSettingsService; - @Lazy private final JwtTokenFactory tokenFactory; private final EntitiesVersionControlService versionControlService; private final TbAutoCommitSettingsService autoCommitSettingsService; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java index 2f157af70e..f5b769e568 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java @@ -19,6 +19,7 @@ import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.cluster.TbClusterService; import org.thingsboard.server.common.msg.notification.NotificationRuleProcessor; import org.thingsboard.server.dao.asset.AssetProfileService; @@ -150,6 +151,9 @@ public class EdgeContextComponent { @Autowired private ResourceService resourceService; + @Autowired + private RateLimitService rateLimitService; + @Autowired private NotificationRuleProcessor notificationRuleProcessor; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java index 4924827e35..2580d399af 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java @@ -35,6 +35,7 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.limit.LimitedApi; import org.thingsboard.server.common.data.notification.rule.trigger.EdgeCommunicationFailureTrigger; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; @@ -109,6 +110,7 @@ public final class EdgeGrpcSession implements Closeable { private static final String QUEUE_START_TS_ATTR_KEY = "queueStartTs"; private static final String QUEUE_START_SEQ_ID_ATTR_KEY = "queueStartSeqId"; + private static final String RATE_LIMIT_REACHED = "Rate limit reached"; private final UUID sessionId; private final BiConsumer sessionOpenListener; @@ -264,36 +266,51 @@ public final class EdgeGrpcSession implements Closeable { } private void onUplinkMsg(UplinkMsg uplinkMsg) { + if (isRateLimitViolated(uplinkMsg)) { + return; + } ListenableFuture> future = processUplinkMsg(uplinkMsg); Futures.addCallback(future, new FutureCallback<>() { @Override public void onSuccess(@Nullable List result) { - UplinkResponseMsg uplinkResponseMsg = UplinkResponseMsg.newBuilder() - .setUplinkMsgId(uplinkMsg.getUplinkMsgId()) - .setSuccess(true).build(); - sendDownlinkMsg(ResponseMsg.newBuilder() - .setUplinkResponseMsg(uplinkResponseMsg) - .build()); + sendResponseMessage(uplinkMsg.getUplinkMsgId(), true, null); } @Override public void onFailure(Throwable t) { String errorMsg = EdgeUtils.createErrorMsgFromRootCauseAndStackTrace(t); - UplinkResponseMsg uplinkResponseMsg = UplinkResponseMsg.newBuilder() - .setUplinkMsgId(uplinkMsg.getUplinkMsgId()) - .setSuccess(false).setErrorMsg(errorMsg).build(); - sendDownlinkMsg(ResponseMsg.newBuilder() - .setUplinkResponseMsg(uplinkResponseMsg) - .build()); + sendResponseMessage(uplinkMsg.getUplinkMsgId(), false, errorMsg); } }, ctx.getGrpcCallbackExecutorService()); } + private boolean isRateLimitViolated(UplinkMsg uplinkMsg) { + if (!ctx.getRateLimitService().checkRateLimit(LimitedApi.EDGE_UPLINK_MESSAGES, tenantId) || + !ctx.getRateLimitService().checkRateLimit(LimitedApi.EDGE_UPLINK_MESSAGES_PER_EDGE, tenantId, edge.getId())) { + String errorMsg = String.format("Failed to process uplink message. %s", RATE_LIMIT_REACHED); + sendResponseMessage(uplinkMsg.getUplinkMsgId(), false, errorMsg); + return true; + } + return false; + } + + private void sendResponseMessage(int uplinkMsgId, boolean success, String errorMsg) { + UplinkResponseMsg.Builder responseBuilder = UplinkResponseMsg.newBuilder() + .setUplinkMsgId(uplinkMsgId) + .setSuccess(success); + if (errorMsg != null) { + responseBuilder.setErrorMsg(errorMsg); + } + sendDownlinkMsg(ResponseMsg.newBuilder() + .setUplinkResponseMsg(responseBuilder.build()) + .build()); + } + private void onDownlinkResponse(DownlinkResponseMsg msg) { try { if (msg.getSuccess()) { sessionState.getPendingMsgsMap().remove(msg.getDownlinkMsgId()); - log.debug("[{}][{}] Msg has been processed successfully!Msg Id: [{}], Msg: {}", this.tenantId, edge.getRoutingKey(), msg.getDownlinkMsgId(), msg); + log.debug("[{}][{}] Msg has been processed successfully! Msg Id: [{}], Msg: {}", this.tenantId, edge.getRoutingKey(), msg.getDownlinkMsgId(), msg); } else { log.error("[{}][{}] Msg processing failed! Msg Id: [{}], Error msg: {}", this.tenantId, edge.getRoutingKey(), msg.getDownlinkMsgId(), msg.getErrorMsg()); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorV1.java index acdd3f506a..524edbb5a5 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorV1.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/asset/AssetMsgConstructorV1.java @@ -63,6 +63,7 @@ public class AssetMsgConstructorV1 extends BaseAssetMsgConstructor { @Override public AssetProfileUpdateMsg constructAssetProfileUpdatedMsg(UpdateMsgType msgType, AssetProfile assetProfile) { + assetProfile = JacksonUtil.clone(assetProfile); imageService.inlineImageForEdge(assetProfile); AssetProfileUpdateMsg.Builder builder = AssetProfileUpdateMsg.newBuilder() .setMsgType(msgType) @@ -89,4 +90,5 @@ public class AssetMsgConstructorV1 extends BaseAssetMsgConstructor { } return builder.build(); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorV1.java index 3df0d6563f..ec2d216a03 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorV1.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/dashboard/DashboardMsgConstructorV1.java @@ -33,6 +33,7 @@ public class DashboardMsgConstructorV1 extends BaseDashboardMsgConstructor { @Override public DashboardUpdateMsg constructDashboardUpdatedMsg(UpdateMsgType msgType, Dashboard dashboard) { + dashboard = JacksonUtil.clone(dashboard); imageService.inlineImagesForEdge(dashboard); DashboardUpdateMsg.Builder builder = DashboardUpdateMsg.newBuilder() .setMsgType(msgType) @@ -52,4 +53,5 @@ public class DashboardMsgConstructorV1 extends BaseDashboardMsgConstructor { } return builder.build(); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorV1.java index 35c07abc5a..0e8b7452de 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorV1.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/device/DeviceMsgConstructorV1.java @@ -95,6 +95,7 @@ public class DeviceMsgConstructorV1 extends BaseDeviceMsgConstructor { @Override public DeviceProfileUpdateMsg constructDeviceProfileUpdatedMsg(UpdateMsgType msgType, DeviceProfile deviceProfile) { + deviceProfile = JacksonUtil.clone(deviceProfile); imageService.inlineImageForEdge(deviceProfile); DeviceProfileUpdateMsg.Builder builder = DeviceProfileUpdateMsg.newBuilder() .setMsgType(msgType) @@ -140,4 +141,5 @@ public class DeviceMsgConstructorV1 extends BaseDeviceMsgConstructor { } return builder.build(); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorV1.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorV1.java index 92fdb32fee..0201f6276b 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorV1.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/widget/WidgetMsgConstructorV1.java @@ -43,6 +43,7 @@ public class WidgetMsgConstructorV1 extends BaseWidgetMsgConstructor { @Override public WidgetsBundleUpdateMsg constructWidgetsBundleUpdateMsg(UpdateMsgType msgType, WidgetsBundle widgetsBundle, List widgets) { + widgetsBundle = JacksonUtil.clone(widgetsBundle); imageService.inlineImageForEdge(widgetsBundle); WidgetsBundleUpdateMsg.Builder builder = WidgetsBundleUpdateMsg.newBuilder() .setMsgType(msgType) @@ -68,6 +69,7 @@ public class WidgetMsgConstructorV1 extends BaseWidgetMsgConstructor { @Override public WidgetTypeUpdateMsg constructWidgetTypeUpdateMsg(UpdateMsgType msgType, WidgetTypeDetails widgetTypeDetails, EdgeVersion edgeVersion) { + widgetTypeDetails = JacksonUtil.clone(widgetTypeDetails); imageService.inlineImagesForEdge(widgetTypeDetails); WidgetTypeUpdateMsg.Builder builder = WidgetTypeUpdateMsg.newBuilder() .setMsgType(msgType) @@ -109,4 +111,5 @@ public class WidgetMsgConstructorV1 extends BaseWidgetMsgConstructor { } return builder.build(); } + } diff --git a/application/src/main/java/org/thingsboard/server/service/mail/RefreshTokenExpCheckService.java b/application/src/main/java/org/thingsboard/server/service/mail/RefreshTokenExpCheckService.java index 9dfa673d58..32fe4eb15c 100644 --- a/application/src/main/java/org/thingsboard/server/service/mail/RefreshTokenExpCheckService.java +++ b/application/src/main/java/org/thingsboard/server/service/mail/RefreshTokenExpCheckService.java @@ -35,6 +35,7 @@ import org.thingsboard.server.queue.util.TbCoreComponent; import java.io.IOException; import java.time.Duration; import java.time.Instant; +import java.util.concurrent.TimeUnit; import static org.thingsboard.server.common.data.mail.MailOauth2Provider.OFFICE_365; @@ -46,7 +47,9 @@ public class RefreshTokenExpCheckService { public static final int AZURE_DEFAULT_REFRESH_TOKEN_LIFETIME_IN_DAYS = 90; private final AdminSettingsService adminSettingsService; - @Scheduled(initialDelayString = "#{T(org.apache.commons.lang3.RandomUtils).nextLong(0, ${mail.oauth2.refreshTokenCheckingInterval})}", fixedDelayString = "${mail.oauth2.refreshTokenCheckingInterval}") + @Scheduled(initialDelayString = "#{T(org.apache.commons.lang3.RandomUtils).nextLong(0, ${mail.oauth2.refreshTokenCheckingInterval})}", + fixedDelayString = "${mail.oauth2.refreshTokenCheckingInterval}", + timeUnit = TimeUnit.SECONDS) public void check() throws IOException { AdminSettings settings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, "mail"); if (settings != null && settings.getJsonValue().has("enableOauth2") && settings.getJsonValue().get("enableOauth2").asBoolean()) { @@ -65,8 +68,8 @@ public class RefreshTokenExpCheckService { new GenericUrl(tokenUri), refreshToken) .setClientAuthentication(new ClientParametersAuthentication(clientId, clientSecret)) .execute(); - ((ObjectNode)jsonValue).put("refreshToken", tokenResponse.getRefreshToken()); - ((ObjectNode)jsonValue).put("refreshTokenExpires", Instant.now().plus(Duration.ofDays(AZURE_DEFAULT_REFRESH_TOKEN_LIFETIME_IN_DAYS)).toEpochMilli()); + ((ObjectNode) jsonValue).put("refreshToken", tokenResponse.getRefreshToken()); + ((ObjectNode) jsonValue).put("refreshTokenExpires", Instant.now().plus(Duration.ofDays(AZURE_DEFAULT_REFRESH_TOKEN_LIFETIME_IN_DAYS)).toEpochMilli()); adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, settings); } } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java index 5a9e3fc3e1..25a344b775 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/DefaultNotificationCenter.java @@ -21,6 +21,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.thingsboard.rule.engine.api.NotificationCenter; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.id.EntityId; @@ -62,7 +63,6 @@ import org.thingsboard.server.dao.notification.NotificationService; import org.thingsboard.server.dao.notification.NotificationSettingsService; import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; -import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.queue.common.TbProtoQueueMsg; import org.thingsboard.server.queue.discovery.TopicService; @@ -204,6 +204,7 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple private void processNotificationRequestAsync(NotificationProcessingContext ctx, List targets, FutureCallback callback) { notificationExecutor.submit(() -> { + long startTs = System.currentTimeMillis(); NotificationRequestId requestId = ctx.getRequest().getId(); for (NotificationTarget target : targets) { try { @@ -219,9 +220,16 @@ public class DefaultNotificationCenter extends AbstractSubscriptionService imple return; } } - log.debug("[{}] Notification request processing is finished", requestId); NotificationRequestStats stats = ctx.getStats(); + long time = System.currentTimeMillis() - startTs; + int sent = stats.getTotalSent().get(); + int errors = stats.getTotalErrors().get(); + if (errors > 0) { + log.info("[{}][{}] Notification request processing finished in {} ms (sent: {}, errors: {})", ctx.getTenantId(), requestId, time, sent, errors); + } else { + log.info("[{}][{}] Notification request processing finished in {} ms (sent: {})", ctx.getTenantId(), requestId, time, sent); + } updateRequestStats(ctx, requestId, stats); if (callback != null) { callback.onSuccess(stats); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/channels/MobileAppNotificationChannel.java b/application/src/main/java/org/thingsboard/server/service/notification/channels/MobileAppNotificationChannel.java index 3f2fcd40f5..646556372a 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/channels/MobileAppNotificationChannel.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/channels/MobileAppNotificationChannel.java @@ -15,6 +15,8 @@ */ package org.thingsboard.server.service.notification.channels; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.base.Strings; import com.google.firebase.messaging.FirebaseMessagingException; import com.google.firebase.messaging.MessagingErrorCode; import lombok.RequiredArgsConstructor; @@ -61,14 +63,7 @@ public class MobileAppNotificationChannel implements NotificationChannel data = Optional.ofNullable(processedTemplate.getAdditionalConfig()) - .map(JacksonUtil::toFlatMap).orElseGet(HashMap::new); - Optional.ofNullable(ctx.getRequest().getInfo()) - .map(NotificationInfo::getStateEntityId) - .ifPresent(stateEntityId -> { - data.put("stateEntityId", stateEntityId.getId().toString()); - data.put("stateEntityType", stateEntityId.getEntityType().name()); - }); + Map data = getNotificationData(processedTemplate, ctx); for (String token : mobileSessions.keySet()) { try { firebaseService.sendMessage(ctx.getTenantId(), credentials, token, subject, body, data); @@ -87,6 +82,37 @@ public class MobileAppNotificationChannel implements NotificationChannel getNotificationData(MobileAppDeliveryMethodNotificationTemplate processedTemplate, NotificationProcessingContext ctx) { + Map data = Optional.ofNullable(processedTemplate.getAdditionalConfig()) + .filter(JsonNode::isObject).map(JacksonUtil::toFlatMap).orElseGet(HashMap::new); + NotificationInfo info = ctx.getRequest().getInfo(); + if (info == null) { + return data; + } + Optional.ofNullable(info.getStateEntityId()).ifPresent(stateEntityId -> { + data.put("stateEntityId", stateEntityId.getId().toString()); + data.put("stateEntityType", stateEntityId.getEntityType().name()); + if (!"true".equals(data.get("onClick.enabled")) && info.getDashboardId() != null) { + data.put("onClick.enabled", "true"); + data.put("onClick.linkType", "DASHBOARD"); + data.put("onClick.setEntityIdInState", "true"); + data.put("onClick.dashboardId", info.getDashboardId().toString()); + } + }); + data.put("notificationType", ctx.getNotificationType().name()); + switch (ctx.getNotificationType()) { + case ALARM: + case ALARM_ASSIGNMENT: + case ALARM_COMMENT: + info.getTemplateData().forEach((key, value) -> { + data.put("info." + key, value); + }); + break; + } + data.replaceAll((key, value) -> Strings.nullToEmpty(value)); + return data; + } + @Override public void check(TenantId tenantId) throws Exception { NotificationSettings systemSettings = notificationSettingsService.findNotificationSettings(TenantId.SYS_TENANT_ID); diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java index 98981d0bc0..9cc7416e34 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmAssignmentTriggerProcessor.java @@ -65,6 +65,7 @@ public class AlarmAssignmentTriggerProcessor implements NotificationRuleTriggerP .alarmSeverity(alarmInfo.getSeverity()) .alarmStatus(alarmInfo.getStatus()) .alarmCustomerId(alarmInfo.getCustomerId()) + .dashboardId(alarmInfo.getDashboardId()) .build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java index a1199214ae..392f68dc9e 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmCommentTriggerProcessor.java @@ -76,6 +76,7 @@ public class AlarmCommentTriggerProcessor implements NotificationRuleTriggerProc .alarmSeverity(alarm.getSeverity()) .alarmStatus(alarm.getStatus()) .alarmCustomerId(alarm.getCustomerId()) + .dashboardId(alarm.getDashboardId()) .build(); } diff --git a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java index 8022f6cbb3..99d58d40a9 100644 --- a/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/notification/rule/trigger/AlarmTriggerProcessor.java @@ -22,11 +22,11 @@ import org.thingsboard.server.common.data.alarm.AlarmInfo; import org.thingsboard.server.common.data.alarm.AlarmStatusFilter; import org.thingsboard.server.common.data.notification.info.AlarmNotificationInfo; import org.thingsboard.server.common.data.notification.info.RuleOriginatedNotificationInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.AlarmTrigger; import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmNotificationRuleTriggerConfig.AlarmAction; import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmNotificationRuleTriggerConfig.ClearRule; import org.thingsboard.server.common.data.notification.rule.trigger.config.NotificationRuleTriggerType; -import org.thingsboard.server.common.data.notification.rule.trigger.AlarmTrigger; import static org.apache.commons.collections.CollectionUtils.isNotEmpty; import static org.thingsboard.server.common.data.util.CollectionsUtil.emptyOrContains; @@ -111,6 +111,7 @@ public class AlarmTriggerProcessor implements NotificationRuleTriggerProcessor jwtSettingsService, + JwtSettingsService jwtSettingsService, NotificationSchedulerService notificationSchedulerService, NotificationRuleProcessor notificationRuleProcessor, TbImageService imageService) { diff --git a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java index afdd150a59..13de09bb2d 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/DefaultTbRuleEngineConsumerService.java @@ -54,6 +54,7 @@ import org.thingsboard.server.service.queue.processing.AbstractConsumerService; import org.thingsboard.server.service.queue.ruleengine.TbRuleEngineConsumerContext; import org.thingsboard.server.service.queue.ruleengine.TbRuleEngineQueueConsumerManager; import org.thingsboard.server.service.rpc.TbRuleEngineDeviceRpcService; +import org.thingsboard.server.service.security.auth.jwt.settings.JwtSettingsService; import javax.annotation.PostConstruct; import java.util.ArrayList; @@ -85,9 +86,11 @@ public class DefaultTbRuleEngineConsumerService extends AbstractConsumerService< TbAssetProfileCache assetProfileCache, TbTenantProfileCache tenantProfileCache, TbApiUsageStateService apiUsageStateService, - PartitionService partitionService, ApplicationEventPublisher eventPublisher) { + PartitionService partitionService, + ApplicationEventPublisher eventPublisher, + JwtSettingsService jwtSettingsService) { super(actorContext, encodingService, tenantProfileCache, deviceProfileCache, assetProfileCache, apiUsageStateService, partitionService, - eventPublisher, tbRuleEngineQueueFactory.createToRuleEngineNotificationsMsgConsumer(), Optional.empty()); + eventPublisher, tbRuleEngineQueueFactory.createToRuleEngineNotificationsMsgConsumer(), jwtSettingsService); this.ctx = ctx; this.tbDeviceRpcService = tbDeviceRpcService; this.queueService = queueService; diff --git a/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java b/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java index 4a348167d0..26e23549e3 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/processing/AbstractConsumerService.java @@ -49,7 +49,6 @@ import org.thingsboard.server.service.security.auth.jwt.settings.JwtSettingsServ import javax.annotation.PreDestroy; import java.util.List; -import java.util.Optional; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -75,14 +74,14 @@ public abstract class AbstractConsumerService> nfConsumer; - protected final Optional jwtSettingsService; + protected final JwtSettingsService jwtSettingsService; public AbstractConsumerService(ActorSystemContext actorContext, DataDecodingEncodingService encodingService, TbTenantProfileCache tenantProfileCache, TbDeviceProfileCache deviceProfileCache, TbAssetProfileCache assetProfileCache, TbApiUsageStateService apiUsageStateService, PartitionService partitionService, ApplicationEventPublisher eventPublisher, - TbQueueConsumer> nfConsumer, Optional jwtSettingsService) { + TbQueueConsumer> nfConsumer, JwtSettingsService jwtSettingsService) { this.actorContext = actorContext; this.encodingService = encodingService; this.tenantProfileCache = tenantProfileCache; @@ -181,7 +180,7 @@ public abstract class AbstractConsumerService tbClusterService; private final Optional notificationCenter; private final JwtSettingsValidator jwtSettingsValidator; @@ -107,11 +104,13 @@ public class DefaultJwtSettingsService implements JwtSettingsService { @Override public JwtSettings reloadJwtSettings() { + log.trace("Executing reloadJwtSettings"); return getJwtSettings(true); } @Override public JwtSettings getJwtSettings() { + log.trace("Executing getJwtSettings"); return getJwtSettings(false); } diff --git a/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java b/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java index 2570b80f9f..95a2c63f52 100644 --- a/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java +++ b/application/src/test/java/org/thingsboard/server/service/limits/RateLimitServiceTest.java @@ -71,6 +71,8 @@ public class RateLimitServiceTest { profileConfiguration.setCassandraQueryTenantRateLimitsConfiguration(rateLimit); profileConfiguration.setEdgeEventRateLimits(rateLimit); profileConfiguration.setEdgeEventRateLimitsPerEdge(rateLimit); + profileConfiguration.setEdgeUplinkMessagesRateLimits(rateLimit); + profileConfiguration.setEdgeUplinkMessagesRateLimitsPerEdge(rateLimit); updateTenantProfileConfiguration(profileConfiguration); for (LimitedApi limitedApi : List.of( @@ -80,7 +82,9 @@ public class RateLimitServiceTest { LimitedApi.REST_REQUESTS_PER_CUSTOMER, LimitedApi.CASSANDRA_QUERIES, LimitedApi.EDGE_EVENTS, - LimitedApi.EDGE_EVENTS_PER_EDGE + LimitedApi.EDGE_EVENTS_PER_EDGE, + LimitedApi.EDGE_UPLINK_MESSAGES, + LimitedApi.EDGE_UPLINK_MESSAGES_PER_EDGE )) { testRateLimits(limitedApi, max, tenantId); } diff --git a/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java index f54b5d9b7b..b945c162df 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/AbstractNotificationApiTest.java @@ -60,6 +60,7 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.controller.AbstractControllerTest; import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.notification.DefaultNotifications; import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.dao.notification.NotificationRuleService; import org.thingsboard.server.dao.notification.NotificationSettingsService; @@ -99,6 +100,8 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest protected NotificationSettingsService notificationSettingsService; @Autowired protected SqlPartitioningRepository partitioningRepository; + @Autowired + protected DefaultNotifications defaultNotifications; public static final String DEFAULT_NOTIFICATION_SUBJECT = "Just a test"; public static final NotificationType DEFAULT_NOTIFICATION_TYPE = NotificationType.GENERAL; @@ -249,10 +252,14 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest } protected NotificationRule createNotificationRule(NotificationRuleTriggerConfig triggerConfig, String subject, String text, NotificationTargetId... targets) { - NotificationTemplate template = createNotificationTemplate(NotificationType.valueOf(triggerConfig.getTriggerType().toString()), subject, text, NotificationDeliveryMethod.WEB); + return createNotificationRule(triggerConfig, subject, text, List.of(targets), NotificationDeliveryMethod.WEB); + } + + protected NotificationRule createNotificationRule(NotificationRuleTriggerConfig triggerConfig, String subject, String text, List targets, NotificationDeliveryMethod... deliveryMethods) { + NotificationTemplate template = createNotificationTemplate(NotificationType.valueOf(triggerConfig.getTriggerType().toString()), subject, text, deliveryMethods); NotificationRule rule = new NotificationRule(); - rule.setName(triggerConfig.getTriggerType() + " " + Arrays.toString(targets)); + rule.setName(triggerConfig.getTriggerType() + " " + targets); rule.setEnabled(true); rule.setTemplateId(template.getId()); rule.setTriggerType(triggerConfig.getTriggerType()); @@ -260,7 +267,7 @@ public abstract class AbstractNotificationApiTest extends AbstractControllerTest DefaultNotificationRuleRecipientsConfig recipientsConfig = new DefaultNotificationRuleRecipientsConfig(); recipientsConfig.setTriggerType(triggerConfig.getTriggerType()); - recipientsConfig.setTargets(DaoUtil.toUUIDs(List.of(targets))); + recipientsConfig.setTargets(DaoUtil.toUUIDs(targets)); rule.setRecipientsConfig(recipientsConfig); return saveNotificationRule(rule); diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java index 2d93573a25..d3b79029ba 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationApiTest.java @@ -30,8 +30,12 @@ import org.springframework.web.client.RestTemplate; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.rule.engine.api.NotificationCenter; import org.thingsboard.rule.engine.api.notification.FirebaseService; +import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.alarm.Alarm; +import org.thingsboard.server.common.data.alarm.AlarmComment; +import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.NotificationRequestId; @@ -49,6 +53,7 @@ import org.thingsboard.server.common.data.notification.NotificationRequestStats; import org.thingsboard.server.common.data.notification.NotificationRequestStatus; import org.thingsboard.server.common.data.notification.NotificationType; import org.thingsboard.server.common.data.notification.info.EntityActionNotificationInfo; +import org.thingsboard.server.common.data.notification.rule.trigger.config.AlarmCommentNotificationRuleTriggerConfig; import org.thingsboard.server.common.data.notification.settings.MobileAppNotificationDeliveryMethodConfig; import org.thingsboard.server.common.data.notification.settings.NotificationSettings; import org.thingsboard.server.common.data.notification.settings.SlackNotificationDeliveryMethodConfig; @@ -771,6 +776,55 @@ public class NotificationApiTest extends AbstractNotificationApiTest { verifyNoMoreInteractions(firebaseService); } + @Test + public void testMobileAppNotifications_ruleBased() throws Exception { + loginSysAdmin(); + MobileAppNotificationDeliveryMethodConfig config = new MobileAppNotificationDeliveryMethodConfig(); + config.setFirebaseServiceAccountCredentials("testCredentials"); + saveNotificationSettings(config); + + loginTenantAdmin(); + mobileToken = "tenantFcmToken"; + doPost("/api/user/mobile/session", new MobileSessionInfo()).andExpect(status().isOk()); + + createNotificationRule(AlarmCommentNotificationRuleTriggerConfig.builder().onlyUserComments(true).build(), + DefaultNotifications.alarmComment.getSubject(), DefaultNotifications.alarmComment.getText(), + List.of(createNotificationTarget(tenantAdminUserId).getId()), NotificationDeliveryMethod.MOBILE_APP); + + Device device = createDevice("test", "test"); + UUID alarmDashboardId = UUID.randomUUID(); + Alarm alarm = Alarm.builder() + .type("test") + .tenantId(tenantId) + .originator(device.getId()) + .severity(AlarmSeverity.MAJOR) + .details(JacksonUtil.newObjectNode() + .put("dashboardId", alarmDashboardId.toString())) + .build(); + alarm = doPost("/api/alarm", alarm, Alarm.class); + + AlarmComment comment = new AlarmComment(); + comment.setComment(JacksonUtil.newObjectNode() + .put("text", "text")); + doPost("/api/alarm/" + alarm.getId() + "/comment", comment, AlarmComment.class); + + ArgumentCaptor> msgCaptor = ArgumentCaptor.forClass(Map.class); + await().atMost(30, TimeUnit.SECONDS).untilAsserted(() -> { + verify(firebaseService).sendMessage(eq(tenantId), eq("testCredentials"), + eq("tenantFcmToken"), eq("Comment on 'test' alarm"), + eq(TENANT_ADMIN_EMAIL + " added comment: text"), + msgCaptor.capture()); + }); + Map firebaseMessageData = msgCaptor.getValue(); + assertThat(firebaseMessageData.keySet()).doesNotContainNull().doesNotContain(""); + assertThat(firebaseMessageData.values()).doesNotContainNull(); + assertThat(firebaseMessageData.get("info.userEmail")).isEqualTo(TENANT_ADMIN_EMAIL); + assertThat(firebaseMessageData.get("info.alarmType")).isEqualTo("test"); + assertThat(firebaseMessageData.get("onClick.enabled")).isEqualTo("true"); + assertThat(firebaseMessageData.get("onClick.linkType")).isEqualTo("DASHBOARD"); + assertThat(firebaseMessageData.get("onClick.dashboardId")).isEqualTo(alarmDashboardId.toString()); + } + @Test public void testMobileSettings_tenantLevel() throws Exception { MobileAppNotificationDeliveryMethodConfig config = new MobileAppNotificationDeliveryMethodConfig(); diff --git a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java index 1e95f9add3..9cf73894c2 100644 --- a/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java +++ b/application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java @@ -26,6 +26,7 @@ import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.data.util.Pair; import org.springframework.test.context.TestPropertySource; import org.thingsboard.common.util.JacksonUtil; +import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; @@ -93,7 +94,6 @@ import org.thingsboard.server.dao.notification.DefaultNotifications; import org.thingsboard.server.dao.notification.NotificationRequestService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DaoSqlTest; -import org.thingsboard.server.cache.limits.RateLimitService; import org.thingsboard.server.queue.notification.DefaultNotificationDeduplicationService; import org.thingsboard.server.service.notification.rule.cache.DefaultNotificationRulesCache; import org.thingsboard.server.service.state.DeviceStateService; @@ -140,8 +140,6 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest { @Autowired private NotificationRuleProcessor notificationRuleProcessor; @Autowired - private DefaultNotifications defaultNotifications; - @Autowired private DefaultNotificationRulesCache notificationRulesCache; @Autowired private DeviceStateService deviceStateService; diff --git a/application/src/test/java/org/thingsboard/server/service/security/auth/JwtTokenFactoryTest.java b/application/src/test/java/org/thingsboard/server/service/security/auth/JwtTokenFactoryTest.java index d412fb2f84..2d39dd9905 100644 --- a/application/src/test/java/org/thingsboard/server/service/security/auth/JwtTokenFactoryTest.java +++ b/application/src/test/java/org/thingsboard/server/service/security/auth/JwtTokenFactoryTest.java @@ -37,7 +37,6 @@ import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.UserPrincipal; import org.thingsboard.server.service.security.model.token.AccessJwtToken; import org.thingsboard.server.service.security.model.token.JwtTokenFactory; -import org.thingsboard.server.service.security.model.token.RawAccessJwtToken; import java.util.Calendar; import java.util.Date; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java index da1d8489fd..7094335696 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/alarm/Alarm.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.common.data.alarm; +import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; @@ -30,6 +31,7 @@ import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.AlarmId; import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.UserId; @@ -37,6 +39,8 @@ import org.thingsboard.server.common.data.validation.Length; import org.thingsboard.server.common.data.validation.NoXss; import java.util.List; +import java.util.Optional; +import java.util.UUID; /** * Created by ashvayka on 11.05.17. @@ -160,4 +164,10 @@ public class Alarm extends BaseData implements HasName, HasTenantId, Ha } } + @JsonIgnore + public DashboardId getDashboardId() { + return Optional.ofNullable(getDetails()).map(details -> details.get("dashboardId")) + .filter(JsonNode::isTextual).map(id -> new DashboardId(UUID.fromString(id.asText()))).orElse(null); + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java index 980eb880a0..1a0f038c90 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/limit/LimitedApi.java @@ -33,6 +33,8 @@ public enum LimitedApi { CASSANDRA_QUERIES(DefaultTenantProfileConfiguration::getCassandraQueryTenantRateLimitsConfiguration, "Cassandra queries", true), EDGE_EVENTS(DefaultTenantProfileConfiguration::getEdgeEventRateLimits, "Edge events", true), EDGE_EVENTS_PER_EDGE(DefaultTenantProfileConfiguration::getEdgeEventRateLimitsPerEdge, "Edge events per edge", false), + EDGE_UPLINK_MESSAGES(DefaultTenantProfileConfiguration::getEdgeUplinkMessagesRateLimits, "Edge uplink messages", true), + EDGE_UPLINK_MESSAGES_PER_EDGE(DefaultTenantProfileConfiguration::getEdgeUplinkMessagesRateLimitsPerEdge, "Edge uplink messages per edge", false), PASSWORD_RESET(false, true), TWO_FA_VERIFICATION_CODE_SEND(false, true), TWO_FA_VERIFICATION_CODE_CHECK(false, true), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java index f7cdbda6fc..2bd9c526fd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationRequestStats.java @@ -31,14 +31,20 @@ import java.util.concurrent.atomic.AtomicInteger; public class NotificationRequestStats { private final Map sent; + @JsonIgnore + private final AtomicInteger totalSent; private final Map> errors; + @JsonIgnore + private final AtomicInteger totalErrors; private String error; @JsonIgnore private final Map> processedRecipients; public NotificationRequestStats() { this.sent = new ConcurrentHashMap<>(); + this.totalSent = new AtomicInteger(); this.errors = new ConcurrentHashMap<>(); + this.totalErrors = new AtomicInteger(); this.processedRecipients = new ConcurrentHashMap<>(); } @@ -47,13 +53,16 @@ public class NotificationRequestStats { @JsonProperty("errors") Map> errors, @JsonProperty("error") String error) { this.sent = sent; + this.totalSent = null; this.errors = errors; + this.totalErrors = null; this.error = error; this.processedRecipients = Collections.emptyMap(); } public void reportSent(NotificationDeliveryMethod deliveryMethod, NotificationRecipient recipient) { sent.computeIfAbsent(deliveryMethod, k -> new AtomicInteger()).incrementAndGet(); + totalSent.incrementAndGet(); } public void reportError(NotificationDeliveryMethod deliveryMethod, Throwable error, NotificationRecipient recipient) { @@ -65,6 +74,7 @@ public class NotificationRequestStats { errorMessage = error.getClass().getSimpleName(); } errors.computeIfAbsent(deliveryMethod, k -> new ConcurrentHashMap<>()).put(recipient.getTitle(), errorMessage); + totalErrors.incrementAndGet(); } public void reportProcessed(NotificationDeliveryMethod deliveryMethod, Object recipientId) { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java index cfc91d8fca..5a8fd87933 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/NotificationType.java @@ -31,4 +31,5 @@ public enum NotificationType { RATE_LIMITS, EDGE_CONNECTION, EDGE_COMMUNICATION_FAILURE + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java index c2b12cc17c..13567405d8 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmAssignmentNotificationInfo.java @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.UserId; @@ -55,6 +56,7 @@ public class AlarmAssignmentNotificationInfo implements RuleOriginatedNotificati private AlarmSeverity alarmSeverity; private AlarmStatus alarmStatus; private CustomerId alarmCustomerId; + private DashboardId dashboardId; @Override public Map getTemplateData() { @@ -94,4 +96,9 @@ public class AlarmAssignmentNotificationInfo implements RuleOriginatedNotificati return alarmOriginator; } + @Override + public DashboardId getDashboardId() { + return dashboardId; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java index aed023084f..d819b42c3c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmCommentNotificationInfo.java @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.EntityId; import java.util.Map; @@ -50,6 +51,7 @@ public class AlarmCommentNotificationInfo implements RuleOriginatedNotificationI private AlarmSeverity alarmSeverity; private AlarmStatus alarmStatus; private CustomerId alarmCustomerId; + private DashboardId dashboardId; @Override public Map getTemplateData() { @@ -80,4 +82,9 @@ public class AlarmCommentNotificationInfo implements RuleOriginatedNotificationI return alarmOriginator; } + @Override + public DashboardId getDashboardId() { + return dashboardId; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java index 13b8c2e3ea..fb7fa478ea 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/AlarmNotificationInfo.java @@ -22,6 +22,7 @@ import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.alarm.AlarmSeverity; import org.thingsboard.server.common.data.alarm.AlarmStatus; import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.EntityId; import java.util.Map; @@ -45,6 +46,7 @@ public class AlarmNotificationInfo implements RuleOriginatedNotificationInfo { private boolean acknowledged; private boolean cleared; private CustomerId alarmCustomerId; + private DashboardId dashboardId; @Override public Map getTemplateData() { @@ -70,4 +72,9 @@ public class AlarmNotificationInfo implements RuleOriginatedNotificationInfo { return alarmOriginator; } + @Override + public DashboardId getDashboardId() { + return dashboardId; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/NotificationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/NotificationInfo.java index 14d2a876c0..50e3f05e6d 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/NotificationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/notification/info/NotificationInfo.java @@ -18,6 +18,7 @@ package org.thingsboard.server.common.data.notification.info; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.EntityId; import java.util.Map; @@ -33,4 +34,8 @@ public interface NotificationInfo { return null; } + default DashboardId getDashboardId() { + return null; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java index 3fe110de0b..a0cfbfe1e1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/tenant/profile/DefaultTenantProfileConfiguration.java @@ -83,6 +83,8 @@ public class DefaultTenantProfileConfiguration implements TenantProfileConfigura private String edgeEventRateLimits; private String edgeEventRateLimitsPerEdge; + private String edgeUplinkMessagesRateLimits; + private String edgeUplinkMessagesRateLimitsPerEdge; private int defaultStorageTtlDays; private int alarmsTtlDays; diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDes.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDes.java index b62dae0efa..e199015fea 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDes.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDes.java @@ -146,7 +146,7 @@ public class LwM2MClientSerDes { if (multiInstances) { Map instances = new HashMap<>(); o.get("instances").asObject().forEach(entry -> { - instances.put(Integer.valueOf(entry.getName()), parseValue(type, entry.getValue())); + instances.put(Integer.valueOf(entry.getName()), parseValue(type, entry.getValue().asObject().get("value"))); }); return LwM2mMultipleResource.newResource(id, instances, type); } else { diff --git a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java index 69064d5ff8..58b01b83d6 100644 --- a/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java +++ b/common/transport/lwm2m/src/main/java/org/thingsboard/server/transport/lwm2m/server/uplink/DefaultLwM2mUplinkMsgHandler.java @@ -480,7 +480,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl CountDownLatch latch = new CountDownLatch(targetIds.size()); targetIds.forEach(versionedId -> sendReadRequest(lwM2MClient, versionedId, new TbLwM2MLatchCallback<>(latch, new TbLwM2MReadCallback(this, logService, lwM2MClient, versionedId)))); - latch.await(); + latch.await(config.getTimeout(), TimeUnit.MILLISECONDS); } catch (InterruptedException e) { log.error("[{}] Failed to await Read requests!", lwM2MClient.getEndpoint(), e); } catch (Exception e) { @@ -498,7 +498,7 @@ public class DefaultLwM2mUplinkMsgHandler extends LwM2MExecutorAwareService impl targetIds.forEach(targetId -> sendObserveRequest(lwM2MClient, targetId, new TbLwM2MLatchCallback<>(latch, new TbLwM2MObserveCallback(this, logService, lwM2MClient, targetId)))); - latch.await(); + latch.await(config.getTimeout(), TimeUnit.MILLISECONDS); } catch (InterruptedException e) { log.error("[{}] Failed to await Observe requests!", lwM2MClient.getEndpoint(), e); } catch (Exception e) { diff --git a/common/transport/lwm2m/src/test/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientTest.java b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientTest.java similarity index 100% rename from common/transport/lwm2m/src/test/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientTest.java rename to common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/client/LwM2mClientTest.java diff --git a/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDesTest.java b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDesTest.java new file mode 100644 index 0000000000..8cdd7b5147 --- /dev/null +++ b/common/transport/lwm2m/src/test/java/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDesTest.java @@ -0,0 +1,151 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.transport.lwm2m.server.store.util; + +import org.eclipse.leshan.core.link.Link; +import org.eclipse.leshan.core.node.LwM2mMultipleResource; +import org.eclipse.leshan.core.node.LwM2mResource; +import org.eclipse.leshan.core.node.LwM2mSingleResource; +import org.eclipse.leshan.core.request.Identity; +import org.eclipse.leshan.core.request.WriteRequest; +import org.eclipse.leshan.server.registration.Registration; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; +import org.thingsboard.server.common.data.TbResource; +import org.thingsboard.server.common.data.device.data.PowerMode; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.DeviceId; +import org.thingsboard.server.common.data.id.DeviceProfileId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.transport.TransportResourceCache; +import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; +import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; +import org.thingsboard.server.gen.transport.TransportProtos; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportContext; +import org.thingsboard.server.transport.lwm2m.server.LwM2mTransportServerHelper; +import org.thingsboard.server.transport.lwm2m.server.LwM2mVersionedModelProvider; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; +import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext; +import org.thingsboard.server.transport.lwm2m.server.client.ResourceValue; + +import java.net.InetSocketAddress; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class LwM2MClientSerDesTest { + + @Test + public void serializeDeserialize() throws Exception { + LwM2mClient client = new LwM2mClient("nodeId", "testEndpoint"); + + TransportDeviceInfo tdi = new TransportDeviceInfo(); + tdi.setPowerMode(PowerMode.PSM); + tdi.setPsmActivityTimer(10000L); + tdi.setPagingTransmissionWindow(2000L); + tdi.setEdrxCycle(3000L); + tdi.setTenantId(TenantId.fromUUID(UUID.randomUUID())); + tdi.setCustomerId(new CustomerId(UUID.randomUUID())); + tdi.setDeviceId(new DeviceId(UUID.randomUUID())); + tdi.setDeviceProfileId(new DeviceProfileId(UUID.randomUUID())); + tdi.setDeviceName("testDevice"); + tdi.setDeviceType("testType"); + ValidateDeviceCredentialsResponse credentialsResponse = ValidateDeviceCredentialsResponse.builder() + .deviceInfo(tdi) + .build(); + + client.init(credentialsResponse, UUID.randomUUID()); + + Registration registration = + new Registration.Builder("test", "testEndpoint", Identity + .unsecure(new InetSocketAddress(1000))) + .supportedContentFormats() + .supportedObjects(Map.of(15, "1.0", 17, "1.0")) + .objectLinks(new Link[]{new Link("/")}) + .build(); + + client.setRegistration(registration); + client.setState(LwM2MClientState.REGISTERED); + client.getSharedAttributes().put("key1", TransportProtos.TsKvProto.newBuilder().setTs(0).setKv(TransportProtos.KeyValueProto.newBuilder().setStringV("test").build()).build()); + client.getSharedAttributes().put("key2", TransportProtos.TsKvProto.newBuilder().setTs(1).setKv(TransportProtos.KeyValueProto.newBuilder().setDoubleV(1.02).build()).build()); + + TransportResourceCache resourceCache = mock(TransportResourceCache.class); + LwM2mTransportContext context = mock(LwM2mTransportContext.class); + LwM2mClientContext clientContext = mock(LwM2mClientContext.class); + + var provider = new LwM2mVersionedModelProvider(clientContext, new LwM2mTransportServerHelper(context), context); + + TbResource resource15 = new TbResource(); + resource15.setData(Files.readAllBytes(Path.of(this.getClass().getClassLoader().getResource("15.xml").toURI()))); + TbResource resource17 = new TbResource(); + resource17.setData(Files.readAllBytes(Path.of(this.getClass().getClassLoader().getResource("17.xml").toURI()))); + + when(resourceCache.get(any(), any(), eq("15_1.0"))).thenReturn(Optional.of(resource15)); + when(resourceCache.get(any(), any(), eq("17_1.0"))).thenReturn(Optional.of(resource17)); + when(context.getTransportResourceCache()).thenReturn(resourceCache); + when(clientContext.getClientByEndpoint(any())).thenReturn(client); + + LwM2mResource singleResource = LwM2mSingleResource.newStringResource(15, "testValue"); + LwM2mResource multipleResource = LwM2mMultipleResource.newStringResource(17, Map.of(0, "testValue", 1, "testValue")); + client.saveResourceValue("/15_1.0/0/0", singleResource, provider, WriteRequest.Mode.UPDATE); + client.saveResourceValue("/17_1.0/0/0", multipleResource, provider, WriteRequest.Mode.UPDATE); + + byte[] bytes = LwM2MClientSerDes.serialize(client); + Assert.assertNotNull(bytes); + + LwM2mClient desClient = LwM2MClientSerDes.deserialize(bytes); + + assertEquals(client.getNodeId(), desClient.getNodeId()); + assertEquals(client.getEndpoint(), desClient.getEndpoint()); + assertEquals(client.getSharedAttributes(), desClient.getSharedAttributes()); + assertEquals(client.getKeyTsLatestMap(), desClient.getKeyTsLatestMap()); + assertEquals(client.getTenantId(), desClient.getTenantId()); + assertEquals(client.getProfileId(), desClient.getProfileId()); + assertEquals(client.getDeviceId(), desClient.getDeviceId()); + assertEquals(client.getState(), desClient.getState()); + assertEquals(client.getSession(), desClient.getSession()); + assertEquals(client.getPowerMode(), desClient.getPowerMode()); + assertEquals(client.getPsmActivityTimer(), desClient.getPsmActivityTimer()); + assertEquals(client.getPagingTransmissionWindow(), desClient.getPagingTransmissionWindow()); + assertEquals(client.getEdrxCycle(), desClient.getEdrxCycle()); + assertEquals(client.getRegistration(), desClient.getRegistration()); + assertEquals(client.isAsleep(), desClient.isAsleep()); + assertEquals(client.getLastUplinkTime(), desClient.getLastUplinkTime()); + assertEquals(client.getSleepTask(), desClient.getSleepTask()); + assertEquals(client.getClientSupportContentFormats(), desClient.getClientSupportContentFormats()); + assertEquals(client.getDefaultContentFormat(), desClient.getDefaultContentFormat()); + assertEquals(client.getRetryAttempts().get(), desClient.getRetryAttempts().get()); + assertEquals(client.getLastSentRpcId(), desClient.getLastSentRpcId()); + + Map expectedResources = client.getResources(); + Map actualResources = desClient.getResources(); + assertNotNull(actualResources); + assertEquals(expectedResources.size(), actualResources.size()); + expectedResources.forEach((key, value) -> assertEquals(value.toString(), actualResources.get(key).toString())); + } + +} \ No newline at end of file diff --git a/common/transport/lwm2m/src/test/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDesTest.java b/common/transport/lwm2m/src/test/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDesTest.java deleted file mode 100644 index 83181d978a..0000000000 --- a/common/transport/lwm2m/src/test/org/thingsboard/server/transport/lwm2m/server/store/util/LwM2MClientSerDesTest.java +++ /dev/null @@ -1,101 +0,0 @@ -/** - * 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. - */ -package org.thingsboard.server.transport.lwm2m.server.store.util; - -import org.eclipse.leshan.core.link.Link; -import org.eclipse.leshan.core.request.Identity; -import org.eclipse.leshan.server.registration.Registration; -import org.junit.Assert; -import org.junit.Test; -import org.thingsboard.server.common.data.device.data.PowerMode; -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.DeviceProfileId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.transport.auth.TransportDeviceInfo; -import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; -import org.thingsboard.server.gen.transport.TransportProtos; -import org.thingsboard.server.transport.lwm2m.server.client.LwM2MClientState; -import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClient; - -import java.net.InetSocketAddress; -import java.util.UUID; - -public class LwM2MClientSerDesTest { - - @Test - public void serializeDeserialize() { - LwM2mClient client = new LwM2mClient("nodeId", "testEndpoint"); - - TransportDeviceInfo tdi = new TransportDeviceInfo(); - tdi.setPowerMode(PowerMode.PSM); - tdi.setPsmActivityTimer(10000L); - tdi.setPagingTransmissionWindow(2000L); - tdi.setEdrxCycle(3000L); - tdi.setTenantId(TenantId.fromUUID(UUID.randomUUID())); - tdi.setCustomerId(new CustomerId(UUID.randomUUID())); - tdi.setDeviceId(new DeviceId(UUID.randomUUID())); - tdi.setDeviceProfileId(new DeviceProfileId(UUID.randomUUID())); - tdi.setDeviceName("testDevice"); - tdi.setDeviceType("testType"); - ValidateDeviceCredentialsResponse credentialsResponse = ValidateDeviceCredentialsResponse.builder() - .deviceInfo(tdi) - .build(); - - client.init(credentialsResponse, UUID.randomUUID()); - - Registration registration = - new Registration.Builder("test", "testEndpoint", Identity - .unsecure(new InetSocketAddress(1000))) - .supportedContentFormats() - .objectLinks(new Link[]{new Link("/")}) - .build(); - - client.setRegistration(registration); - client.setState(LwM2MClientState.REGISTERED); - - client.getSharedAttributes().put("key1", TransportProtos.TsKvProto.newBuilder().setTs(0).setKv(TransportProtos.KeyValueProto.newBuilder().setStringV("test").build()).build()); - client.getSharedAttributes().put("key2", TransportProtos.TsKvProto.newBuilder().setTs(1).setKv(TransportProtos.KeyValueProto.newBuilder().setDoubleV(1.02).build()).build()); - - byte[] bytes = LwM2MClientSerDes.serialize(client); - Assert.assertNotNull(bytes); - - LwM2mClient desClient = LwM2MClientSerDes.deserialize(bytes); - - Assert.assertEquals(client.getNodeId(), desClient.getNodeId()); - Assert.assertEquals(client.getEndpoint(), desClient.getEndpoint()); - Assert.assertEquals(client.getResources(), desClient.getResources()); - Assert.assertEquals(client.getSharedAttributes(), desClient.getSharedAttributes()); - Assert.assertEquals(client.getKeyTsLatestMap(), desClient.getKeyTsLatestMap()); - Assert.assertEquals(client.getTenantId(), desClient.getTenantId()); - Assert.assertEquals(client.getProfileId(), desClient.getProfileId()); - Assert.assertEquals(client.getDeviceId(), desClient.getDeviceId()); - Assert.assertEquals(client.getState(), desClient.getState()); - Assert.assertEquals(client.getSession(), desClient.getSession()); - Assert.assertEquals(client.getPowerMode(), desClient.getPowerMode()); - Assert.assertEquals(client.getPsmActivityTimer(), desClient.getPsmActivityTimer()); - Assert.assertEquals(client.getPagingTransmissionWindow(), desClient.getPagingTransmissionWindow()); - Assert.assertEquals(client.getEdrxCycle(), desClient.getEdrxCycle()); - Assert.assertEquals(client.getRegistration(), desClient.getRegistration()); - Assert.assertEquals(client.isAsleep(), desClient.isAsleep()); - Assert.assertEquals(client.getLastUplinkTime(), desClient.getLastUplinkTime()); - Assert.assertEquals(client.getSleepTask(), desClient.getSleepTask()); - Assert.assertEquals(client.getClientSupportContentFormats(), desClient.getClientSupportContentFormats()); - Assert.assertEquals(client.getDefaultContentFormat(), desClient.getDefaultContentFormat()); - Assert.assertEquals(client.getRetryAttempts().get(), desClient.getRetryAttempts().get()); - Assert.assertEquals(client.getLastSentRpcId(), desClient.getLastSentRpcId()); - } -} \ No newline at end of file diff --git a/common/transport/lwm2m/src/test/resources/15.xml b/common/transport/lwm2m/src/test/resources/15.xml new file mode 100644 index 0000000000..11c018b3ac --- /dev/null +++ b/common/transport/lwm2m/src/test/resources/15.xml @@ -0,0 +1,26 @@ + + + + Test 15 + + 15 + urn:oma:lwm2m:oma:15:1.0 + 1.0 + 1.0 + Multiple + Optional + + + Test + R + Single + Mandatory + String + + + + + + + + diff --git a/common/transport/lwm2m/src/test/resources/17.xml b/common/transport/lwm2m/src/test/resources/17.xml new file mode 100644 index 0000000000..8417c66fb4 --- /dev/null +++ b/common/transport/lwm2m/src/test/resources/17.xml @@ -0,0 +1,26 @@ + + + + Test 17 + + 17 + urn:oma:lwm2m:oma:17:1.0 + 1.0 + 1.0 + Multiple + Mandatory + + + Test + R + Multiple + Optional + String + + + + + + + + diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java index 1666015a48..8058b0cee5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/CachedAttributesService.java @@ -113,15 +113,15 @@ public class CachedAttributesService implements AttributesService { validate(entityId, scope); Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey); - AttributeCacheKey attributeCacheKey = new AttributeCacheKey(scope, entityId, attributeKey); - TbCacheValueWrapper cachedAttributeValue = cache.get(attributeCacheKey); - if (cachedAttributeValue != null) { - hitCounter.increment(); - AttributeKvEntry cachedAttributeKvEntry = cachedAttributeValue.get(); - return Futures.immediateFuture(Optional.ofNullable(cachedAttributeKvEntry)); - } else { - missCounter.increment(); - return cacheExecutor.submit(() -> { + return cacheExecutor.submit(() -> { + AttributeCacheKey attributeCacheKey = new AttributeCacheKey(scope, entityId, attributeKey); + TbCacheValueWrapper cachedAttributeValue = cache.get(attributeCacheKey); + if (cachedAttributeValue != null) { + hitCounter.increment(); + AttributeKvEntry cachedAttributeKvEntry = cachedAttributeValue.get(); + return Optional.ofNullable(cachedAttributeKvEntry); + } else { + missCounter.increment(); var cacheTransaction = cache.newTransactionForKey(attributeCacheKey); try { Optional result = attributesDao.find(tenantId, entityId, scope, attributeKey); @@ -133,8 +133,8 @@ public class CachedAttributesService implements AttributesService { log.debug("Could not find attribute from cache: [{}] [{}] [{}]", entityId, scope, attributeKey, e); throw e; } - }); - } + } + }); } @Override @@ -207,7 +207,8 @@ public class CachedAttributesService implements AttributesService { @Override public ListenableFuture> findAll(TenantId tenantId, EntityId entityId, String scope) { validate(entityId, scope); - return Futures.immediateFuture(attributesDao.findAll(tenantId, entityId, scope)); + // We can`t watch on cache because the keys are unknown. + return jpaExecutorService.submit(() -> attributesDao.findAll(tenantId, entityId, scope)); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java index 77f81d639f..b3a121baf6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/service/Validator.java @@ -27,6 +27,7 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import java.util.List; import java.util.UUID; +import java.util.function.Function; import java.util.regex.Pattern; public class Validator { @@ -46,6 +47,19 @@ public class Validator { } } + /** + * This method validate EntityId entity id. If entity id is invalid than throw + * IncorrectParameterException exception + * + * @param entityId the entityId + * @param errorMessageFunction the error message for exception that apply entityId + */ + public static void validateEntityId(EntityId entityId, Function errorMessageFunction) { + if (entityId == null || entityId.getId() == null) { + throw new IncorrectParameterException(errorMessageFunction.apply(entityId)); + } + } + /** * This method validate String string. If string is invalid than throw * IncorrectParameterException exception @@ -59,6 +73,18 @@ public class Validator { } } + /* + * This method validate String string. If string is invalid than throw + * IncorrectParameterException exception + * + * @param val the value + * @param errorMessageFunction the error message function that apply value + */ + public static void validateString(String val, Function errorMessageFunction) { + if (val == null || val.isEmpty()) { + throw new IncorrectParameterException(errorMessageFunction.apply(val)); + } + } /** * This method validate long value. If value isn't positive than throw diff --git a/dao/src/main/java/org/thingsboard/server/dao/sqlts/SqlTimeseriesLatestDao.java b/dao/src/main/java/org/thingsboard/server/dao/sqlts/SqlTimeseriesLatestDao.java index 112daf0637..f1a58aa417 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sqlts/SqlTimeseriesLatestDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sqlts/SqlTimeseriesLatestDao.java @@ -151,12 +151,12 @@ public class SqlTimeseriesLatestDao extends BaseAbstractSqlTimeseriesDao impleme @Override public ListenableFuture> findLatestOpt(TenantId tenantId, EntityId entityId, String key) { - return Futures.immediateFuture(Optional.ofNullable(doFindLatest(entityId, key))); + return service.submit(() -> Optional.ofNullable(doFindLatest(entityId, key))); } @Override public ListenableFuture findLatest(TenantId tenantId, EntityId entityId, String key) { - return Futures.immediateFuture(getLatestTsKvEntry(entityId, key)); + return service.submit(() -> getLatestTsKvEntry(entityId, key)); } @Override @@ -221,36 +221,29 @@ public class SqlTimeseriesLatestDao extends BaseAbstractSqlTimeseriesDao impleme } protected ListenableFuture getRemoveLatestFuture(TenantId tenantId, EntityId entityId, DeleteTsKvQuery query) { - TsKvEntry latest = doFindLatest(entityId, query.getKey()); - - if (latest == null) { - return Futures.immediateFuture(new TsKvLatestRemovingResult(query.getKey(), false)); - } - - long ts = latest.getTs(); - ListenableFuture removedLatestFuture; - if (ts >= query.getStartTs() && ts < query.getEndTs()) { - TsKvLatestEntity latestEntity = new TsKvLatestEntity(); - latestEntity.setEntityId(entityId.getId()); - latestEntity.setKey(getOrSaveKeyId(query.getKey())); - removedLatestFuture = service.submit(() -> { + ListenableFuture latestFuture = service.submit(() -> doFindLatest(entityId, query.getKey())); + return Futures.transformAsync(latestFuture, latest -> { + if (latest == null) { + return Futures.immediateFuture(new TsKvLatestRemovingResult(query.getKey(), false)); + } + boolean isRemoved = false; + long ts = latest.getTs(); + if (ts >= query.getStartTs() && ts < query.getEndTs()) { + TsKvLatestEntity latestEntity = new TsKvLatestEntity(); + latestEntity.setEntityId(entityId.getId()); + latestEntity.setKey(getOrSaveKeyId(query.getKey())); tsKvLatestRepository.delete(latestEntity); - return true; - }); - } else { - removedLatestFuture = Futures.immediateFuture(false); - } - - return Futures.transformAsync(removedLatestFuture, isRemoved -> { - if (isRemoved && query.getRewriteLatestIfDeleted()) { - return getNewLatestEntryFuture(tenantId, entityId, query); + isRemoved = true; + if (query.getRewriteLatestIfDeleted()) { + return getNewLatestEntryFuture(tenantId, entityId, query); + } } return Futures.immediateFuture(new TsKvLatestRemovingResult(query.getKey(), isRemoved)); }, MoreExecutors.directExecutor()); } protected ListenableFuture> getFindAllLatestFuture(EntityId entityId) { - return Futures.immediateFuture( + return service.submit(() -> DaoUtil.convertDataList(Lists.newArrayList( searchTsKvLatestRepository.findAllByEntityId(entityId.getId())))); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java b/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java index 1f77fa3c1d..cfba6786f3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java @@ -16,7 +16,6 @@ package org.thingsboard.server.dao.timeseries; import com.google.common.base.Function; -import com.google.common.collect.Lists; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; @@ -43,6 +42,7 @@ import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.service.Validator; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -126,18 +126,22 @@ public class BaseTimeseriesService implements TimeseriesService { @Override public ListenableFuture> findLatest(TenantId tenantId, EntityId entityId, Collection keys) { validate(entityId); - List> futures = Lists.newArrayListWithExpectedSize(keys.size()); - keys.forEach(key -> Validator.validateString(key, "Incorrect key " + key)); - keys.forEach(key -> futures.add(timeseriesLatestDao.findLatest(tenantId, entityId, key))); + List> futures = new ArrayList<>(keys.size()); + keys.forEach(key -> Validator.validateString(key, k -> "Incorrect key " + k)); + for (String key : keys) { + futures.add(timeseriesLatestDao.findLatest(tenantId, entityId, key)); + } return Futures.allAsList(futures); } @Override public List findLatestSync(TenantId tenantId, EntityId entityId, Collection keys) { validate(entityId); - List latestEntries = Lists.newArrayListWithExpectedSize(keys.size()); - keys.forEach(key -> Validator.validateString(key, "Incorrect key " + key)); - keys.forEach(key -> latestEntries.add(timeseriesLatestDao.findLatestSync(tenantId, entityId, key))); + List latestEntries = new ArrayList<>(keys.size()); + keys.forEach(key -> Validator.validateString(key, k -> "Incorrect key " + k)); + for (String key : keys) { + latestEntries.add(timeseriesLatestDao.findLatestSync(tenantId, entityId, key)); + } return latestEntries; } @@ -165,7 +169,7 @@ public class BaseTimeseriesService implements TimeseriesService { @Override public ListenableFuture save(TenantId tenantId, EntityId entityId, TsKvEntry tsKvEntry) { validate(entityId); - List> futures = Lists.newArrayListWithExpectedSize(INSERTS_PER_ENTRY); + List> futures = new ArrayList<>(INSERTS_PER_ENTRY); saveAndRegisterFutures(tenantId, futures, entityId, tsKvEntry, 0L); return Futures.transform(Futures.allAsList(futures), SUM_ALL_INTEGERS, MoreExecutors.directExecutor()); } @@ -182,7 +186,7 @@ public class BaseTimeseriesService implements TimeseriesService { private ListenableFuture doSave(TenantId tenantId, EntityId entityId, List tsKvEntries, long ttl, boolean saveLatest) { int inserts = saveLatest ? INSERTS_PER_ENTRY : INSERTS_PER_ENTRY_WITHOUT_LATEST; - List> futures = Lists.newArrayListWithExpectedSize(tsKvEntries.size() * inserts); + List> futures = new ArrayList<>(tsKvEntries.size() * inserts); for (TsKvEntry tsKvEntry : tsKvEntries) { if (saveLatest) { saveAndRegisterFutures(tenantId, futures, entityId, tsKvEntry, ttl); @@ -195,7 +199,7 @@ public class BaseTimeseriesService implements TimeseriesService { @Override public ListenableFuture> saveLatest(TenantId tenantId, EntityId entityId, List tsKvEntries) { - List> futures = Lists.newArrayListWithExpectedSize(tsKvEntries.size()); + List> futures = new ArrayList<>(tsKvEntries.size()); for (TsKvEntry tsKvEntry : tsKvEntries) { futures.add(timeseriesLatestDao.saveLatest(tenantId, entityId, tsKvEntry)); } @@ -242,7 +246,7 @@ public class BaseTimeseriesService implements TimeseriesService { public ListenableFuture> remove(TenantId tenantId, EntityId entityId, List deleteTsKvQueries) { validate(entityId); deleteTsKvQueries.forEach(BaseTimeseriesService::validate); - List> futures = Lists.newArrayListWithExpectedSize(deleteTsKvQueries.size() * DELETES_PER_ENTRY); + List> futures = new ArrayList<>(deleteTsKvQueries.size() * DELETES_PER_ENTRY); for (DeleteTsKvQuery tsKvQuery : deleteTsKvQueries) { deleteAndRegisterFutures(tenantId, futures, entityId, tsKvQuery); } @@ -252,7 +256,7 @@ public class BaseTimeseriesService implements TimeseriesService { @Override public ListenableFuture> removeLatest(TenantId tenantId, EntityId entityId, Collection keys) { validate(entityId); - List> futures = Lists.newArrayListWithExpectedSize(keys.size()); + List> futures = new ArrayList<>(keys.size()); for (String key : keys) { DeleteTsKvQuery query = new BaseDeleteTsKvQuery(key, 0, System.currentTimeMillis(), false); futures.add(timeseriesLatestDao.removeLatest(tenantId, entityId, query)); @@ -281,7 +285,7 @@ public class BaseTimeseriesService implements TimeseriesService { } private static void validate(EntityId entityId) { - Validator.validateEntityId(entityId, "Incorrect entityId " + entityId); + Validator.validateEntityId(entityId, id -> "Incorrect entityId " + id); } private void validate(ReadTsKvQuery query) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetAttributesNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetAttributesNode.java index fab1893702..7cf144ec66 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetAttributesNode.java +++ b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/metadata/TbAbstractGetAttributesNode.java @@ -99,10 +99,10 @@ public abstract class TbAbstractGetAttributesNode>> failuresPairSet = ConcurrentHashMap.newKeySet(); var getKvEntryPairFutures = Futures.allAsList( - getLatestTelemetry(ctx, entityId, TbNodeUtils.processPatterns(config.getLatestTsKeyNames(), msg), failuresPairSet), getAttrAsync(ctx, entityId, CLIENT_SCOPE, TbNodeUtils.processPatterns(config.getClientAttributeNames(), msg), failuresPairSet), getAttrAsync(ctx, entityId, SHARED_SCOPE, TbNodeUtils.processPatterns(config.getSharedAttributeNames(), msg), failuresPairSet), - getAttrAsync(ctx, entityId, SERVER_SCOPE, TbNodeUtils.processPatterns(config.getServerAttributeNames(), msg), failuresPairSet) + getAttrAsync(ctx, entityId, SERVER_SCOPE, TbNodeUtils.processPatterns(config.getServerAttributeNames(), msg), failuresPairSet), + getLatestTelemetry(ctx, entityId, TbNodeUtils.processPatterns(config.getLatestTsKeyNames(), msg), failuresPairSet) ); withCallback(getKvEntryPairFutures, futuresList -> { var msgMetaData = msg.getMetaData().copy(); diff --git a/ui-ngx/package.json b/ui-ngx/package.json index ab60ebd525..c39817749f 100644 --- a/ui-ngx/package.json +++ b/ui-ngx/package.json @@ -55,7 +55,7 @@ "core-js": "^3.29.1", "date-fns": "2.0.0-alpha.27", "dayjs": "1.11.4", - "echarts": "^5.4.3", + "echarts": "^5.5.0", "flot": "https://github.com/thingsboard/flot.git#0.9-work", "flot.curvedlines": "https://github.com/MichaelZinsmaier/CurvedLines.git#master", "font-awesome": "^4.7.0", diff --git a/ui-ngx/patches/echarts+5.5.0.patch b/ui-ngx/patches/echarts+5.5.0.patch new file mode 100644 index 0000000000..39551ac137 --- /dev/null +++ b/ui-ngx/patches/echarts+5.5.0.patch @@ -0,0 +1,209 @@ +diff --git a/node_modules/echarts/lib/component/dataZoom/DataZoomModel.js b/node_modules/echarts/lib/component/dataZoom/DataZoomModel.js +index d6c05f3..d09baed 100644 +--- a/node_modules/echarts/lib/component/dataZoom/DataZoomModel.js ++++ b/node_modules/echarts/lib/component/dataZoom/DataZoomModel.js +@@ -362,7 +362,10 @@ var DataZoomModel = /** @class */function (_super) { + return axisProxy.getDataValueWindow(); + } + } else { +- return this.getAxisProxy(axisDim, axisIndex).getDataValueWindow(); ++ var axisProxy = this.getAxisProxy(axisDim, axisIndex); ++ if (axisProxy) { ++ return axisProxy.getDataValueWindow(); ++ } + } + }; + /** +@@ -381,10 +384,10 @@ var DataZoomModel = /** @class */function (_super) { + var axisInfo = this._targetAxisInfoMap.get(axisDim); + for (var j = 0; j < axisInfo.indexList.length; j++) { + var proxy = this.getAxisProxy(axisDim, axisInfo.indexList[j]); +- if (proxy.hostedBy(this)) { ++ if (proxy && proxy.hostedBy(this)) { + return proxy; + } +- if (!firstProxy) { ++ if (proxy && !firstProxy) { + firstProxy = proxy; + } + } +diff --git a/node_modules/echarts/lib/component/dataZoom/InsideZoomView.js b/node_modules/echarts/lib/component/dataZoom/InsideZoomView.js +index 06469b2..ccfbfa6 100644 +--- a/node_modules/echarts/lib/component/dataZoom/InsideZoomView.js ++++ b/node_modules/echarts/lib/component/dataZoom/InsideZoomView.js +@@ -96,11 +96,14 @@ var getRangeHandlers = { + range[0] = (range[0] - percentPoint) * scale + percentPoint; + range[1] = (range[1] - percentPoint) * scale + percentPoint; + // Restrict range. +- var minMaxSpan = this.dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan(); +- sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan); +- this.range = range; +- if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) { +- return range; ++ var proxy = this.dataZoomModel.findRepresentativeAxisProxy(); ++ if (proxy) { ++ var minMaxSpan = proxy.getMinMaxSpan(); ++ sliderMove(0, range, [0, 100], 0, minMaxSpan.minSpan, minMaxSpan.maxSpan); ++ this.range = range; ++ if (lastRange[0] !== range[0] || lastRange[1] !== range[1]) { ++ return range; ++ } + } + }, + pan: makeMover(function (range, axisModel, coordSysInfo, coordSysMainType, controller, e) { +diff --git a/node_modules/echarts/lib/component/dataZoom/SliderZoomView.js b/node_modules/echarts/lib/component/dataZoom/SliderZoomView.js +index 98912e0..2e809af 100644 +--- a/node_modules/echarts/lib/component/dataZoom/SliderZoomView.js ++++ b/node_modules/echarts/lib/component/dataZoom/SliderZoomView.js +@@ -64,7 +64,7 @@ var DEFAULT_MOVE_HANDLE_SIZE = 7; + var HORIZONTAL = 'horizontal'; + var VERTICAL = 'vertical'; + var LABEL_GAP = 5; +-var SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter']; ++var SHOW_DATA_SHADOW_SERIES_TYPE = ['line', 'bar', 'candlestick', 'scatter', 'custom']; + var REALTIME_ANIMATION_CONFIG = { + easing: 'cubicOut', + duration: 100, +@@ -359,30 +359,33 @@ var SliderZoomView = /** @class */function (_super) { + var result; + var ecModel = this.ecModel; + dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { +- var seriesModels = dataZoomModel.getAxisProxy(axisDim, axisIndex).getTargetSeriesModels(); +- each(seriesModels, function (seriesModel) { +- if (result) { +- return; +- } +- if (showDataShadow !== true && indexOf(SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type')) < 0) { +- return; +- } +- var thisAxis = ecModel.getComponent(getAxisMainType(axisDim), axisIndex).axis; +- var otherDim = getOtherDim(axisDim); +- var otherAxisInverse; +- var coordSys = seriesModel.coordinateSystem; +- if (otherDim != null && coordSys.getOtherAxis) { +- otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse; +- } +- otherDim = seriesModel.getData().mapDimension(otherDim); +- result = { +- thisAxis: thisAxis, +- series: seriesModel, +- thisDim: axisDim, +- otherDim: otherDim, +- otherAxisInverse: otherAxisInverse +- }; +- }, this); ++ var axisProxy = dataZoomModel.getAxisProxy(axisDim, axisIndex); ++ if (axisProxy) { ++ var seriesModels = axisProxy.getTargetSeriesModels(); ++ each(seriesModels, function (seriesModel) { ++ if (result) { ++ return; ++ } ++ if (showDataShadow !== true && indexOf(SHOW_DATA_SHADOW_SERIES_TYPE, seriesModel.get('type')) < 0) { ++ return; ++ } ++ var thisAxis = ecModel.getComponent(getAxisMainType(axisDim), axisIndex).axis; ++ var otherDim = getOtherDim(axisDim); ++ var otherAxisInverse; ++ var coordSys = seriesModel.coordinateSystem; ++ if (otherDim != null && coordSys.getOtherAxis) { ++ otherAxisInverse = coordSys.getOtherAxis(thisAxis).inverse; ++ } ++ otherDim = seriesModel.getData().mapDimension(otherDim); ++ result = { ++ thisAxis: thisAxis, ++ series: seriesModel, ++ thisDim: axisDim, ++ otherDim: otherDim, ++ otherAxisInverse: otherAxisInverse ++ }; ++ }, this); ++ } + }, this); + return result; + }; +@@ -530,12 +533,17 @@ var SliderZoomView = /** @class */function (_super) { + var dataZoomModel = this.dataZoomModel; + var handleEnds = this._handleEnds; + var viewExtend = this._getViewExtent(); +- var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy().getMinMaxSpan(); +- var percentExtent = [0, 100]; +- sliderMove(delta, handleEnds, viewExtend, dataZoomModel.get('zoomLock') ? 'all' : handleIndex, minMaxSpan.minSpan != null ? linearMap(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null, minMaxSpan.maxSpan != null ? linearMap(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null); +- var lastRange = this._range; +- var range = this._range = asc([linearMap(handleEnds[0], viewExtend, percentExtent, true), linearMap(handleEnds[1], viewExtend, percentExtent, true)]); +- return !lastRange || lastRange[0] !== range[0] || lastRange[1] !== range[1]; ++ var proxy = dataZoomModel.findRepresentativeAxisProxy(); ++ if (proxy) { ++ var minMaxSpan = proxy.getMinMaxSpan(); ++ var percentExtent = [0, 100]; ++ sliderMove(delta, handleEnds, viewExtend, dataZoomModel.get('zoomLock') ? 'all' : handleIndex, minMaxSpan.minSpan != null ? linearMap(minMaxSpan.minSpan, percentExtent, viewExtend, true) : null, minMaxSpan.maxSpan != null ? linearMap(minMaxSpan.maxSpan, percentExtent, viewExtend, true) : null); ++ var lastRange = this._range; ++ var range = this._range = asc([linearMap(handleEnds[0], viewExtend, percentExtent, true), linearMap(handleEnds[1], viewExtend, percentExtent, true)]); ++ return !lastRange || lastRange[0] !== range[0] || lastRange[1] !== range[1]; ++ } else { ++ return false; ++ } + }; + SliderZoomView.prototype._updateView = function (nonRealtime) { + var displaybles = this._displayables; +diff --git a/node_modules/echarts/lib/component/dataZoom/dataZoomProcessor.js b/node_modules/echarts/lib/component/dataZoom/dataZoomProcessor.js +index ce98fed..361b138 100644 +--- a/node_modules/echarts/lib/component/dataZoom/dataZoomProcessor.js ++++ b/node_modules/echarts/lib/component/dataZoom/dataZoomProcessor.js +@@ -90,7 +90,10 @@ var dataZoomProcessor = { + // init stage and not after action dispatch handler, because + // reset should be called after seriesData.restoreData. + dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { +- dataZoomModel.getAxisProxy(axisDim, axisIndex).reset(dataZoomModel); ++ var axisProxy = dataZoomModel.getAxisProxy(axisDim, axisIndex); ++ if (axisProxy) { ++ axisProxy.reset(dataZoomModel); ++ } + }); + // Caution: data zoom filtering is order sensitive when using + // percent range and no min/max/scale set on axis. +@@ -107,7 +110,10 @@ var dataZoomProcessor = { + // So we should filter x-axis after reset x-axis immediately, + // and then reset y-axis and filter y-axis. + dataZoomModel.eachTargetAxis(function (axisDim, axisIndex) { +- dataZoomModel.getAxisProxy(axisDim, axisIndex).filterData(dataZoomModel, api); ++ var axisProxy = dataZoomModel.getAxisProxy(axisDim, axisIndex); ++ if (axisProxy) { ++ axisProxy.filterData(dataZoomModel, api); ++ } + }); + }); + ecModel.eachComponent('dataZoom', function (dataZoomModel) { +diff --git a/node_modules/echarts/lib/component/toolbox/feature/DataZoom.js b/node_modules/echarts/lib/component/toolbox/feature/DataZoom.js +index cf8d6bc..9b30ec1 100644 +--- a/node_modules/echarts/lib/component/toolbox/feature/DataZoom.js ++++ b/node_modules/echarts/lib/component/toolbox/feature/DataZoom.js +@@ -109,9 +109,12 @@ var DataZoomFeature = /** @class */function (_super) { + var axisModel = axis.model; + var dataZoomModel = findDataZoom(dimName, axisModel, ecModel); + // Restrict range. +- var minMaxSpan = dataZoomModel.findRepresentativeAxisProxy(axisModel).getMinMaxSpan(); +- if (minMaxSpan.minValueSpan != null || minMaxSpan.maxValueSpan != null) { +- minMax = sliderMove(0, minMax.slice(), axis.scale.getExtent(), 0, minMaxSpan.minValueSpan, minMaxSpan.maxValueSpan); ++ var proxy = dataZoomModel.findRepresentativeAxisProxy(axisModel); ++ if (proxy) { ++ var minMaxSpan = proxy.getMinMaxSpan(); ++ if (minMaxSpan.minValueSpan != null || minMaxSpan.maxValueSpan != null) { ++ minMax = sliderMove(0, minMax.slice(), axis.scale.getExtent(), 0, minMaxSpan.minValueSpan, minMaxSpan.maxValueSpan); ++ } + } + dataZoomModel && (snapshot[dataZoomModel.id] = { + dataZoomId: dataZoomModel.id, +diff --git a/node_modules/echarts/lib/component/tooltip/TooltipView.js b/node_modules/echarts/lib/component/tooltip/TooltipView.js +index b8a9b95..8e4cb2f 100644 +--- a/node_modules/echarts/lib/component/tooltip/TooltipView.js ++++ b/node_modules/echarts/lib/component/tooltip/TooltipView.js +@@ -360,7 +360,7 @@ var TooltipView = /** @class */function (_super) { + each(itemCoordSys.dataByAxis, function (axisItem) { + var axisModel = ecModel.getComponent(axisItem.axisDim + 'Axis', axisItem.axisIndex); + var axisValue = axisItem.value; +- if (!axisModel || axisValue == null) { ++ if (!axisModel || !axisModel.axis || axisValue == null) { + return; + } + var axisValueLabel = axisPointerViewHelper.getValueLabel(axisValue, axisModel.axis, ecModel, axisItem.seriesDataIndices, axisItem.valueLabelOpt); diff --git a/ui-ngx/src/app/core/api/data-aggregator.ts b/ui-ngx/src/app/core/api/data-aggregator.ts index c11c08ad2b..61cb1f1a85 100644 --- a/ui-ngx/src/app/core/api/data-aggregator.ts +++ b/ui-ngx/src/app/core/api/data-aggregator.ts @@ -14,24 +14,23 @@ /// limitations under the License. /// +import { AggKey, IndexedSubscriptionData, } from '@app/shared/models/telemetry/telemetry.models'; import { - AggKey, - IndexedSubscriptionData, -} from '@app/shared/models/telemetry/telemetry.models'; -import { - AggregationType, calculateAggIntervalWithSubscriptionTimeWindow, + AggregationType, + calculateAggIntervalWithSubscriptionTimeWindow, calculateIntervalComparisonEndTime, calculateIntervalEndTime, calculateIntervalStartEndTime, getCurrentTime, - getTime, IntervalMath, + getTime, + IntervalMath, SubscriptionTimewindow } from '@shared/models/time/time.models'; import { UtilsService } from '@core/services/utils.service'; import { deepClone, isDefinedAndNotNull, isNumber, isNumeric } from '@core/utils'; -import Timeout = NodeJS.Timeout; import { DataEntry, DataSet, IndexedData } from '@shared/models/widget.models'; import BTree from 'sorted-btree'; +import Timeout = NodeJS.Timeout; export declare type onAggregatedData = (data: IndexedData, detectChanges: boolean) => void; @@ -318,7 +317,9 @@ export class DataAggregator { this.startTs += tickTs; this.endTs += tickTs; } - this.updateLastInterval(); + if (this.subsTw.aggregation.type !== AggregationType.NONE) { + this.updateLastInterval(); + } this.data = this.updateData(); this.elapsed = this.elapsed - delta * this.aggregationTimeout; } diff --git a/ui-ngx/src/app/core/api/widget-subscription.ts b/ui-ngx/src/app/core/api/widget-subscription.ts index ff16af7740..933d836ffd 100644 --- a/ui-ngx/src/app/core/api/widget-subscription.ts +++ b/ui-ngx/src/app/core/api/widget-subscription.ts @@ -61,9 +61,8 @@ import { deepClone, flatFormattedData, formattedDataFormDatasourceData, - isDefined, isDefinedAndNotNull, - isEqual, + isEqual, isUndefined, parseHttpErrorMessage } from '@core/utils'; import { EntityId } from '@app/shared/models/id/entity-id'; @@ -355,7 +354,7 @@ export class WidgetSubscription implements IWidgetSubscription { } this.units = options.units || ''; - this.decimals = isDefined(options.decimals) ? options.decimals : 2; + this.decimals = isDefinedAndNotNull(options.decimals) ? options.decimals : 2; this.loadingData = false; @@ -1491,7 +1490,8 @@ export class WidgetSubscription implements IWidgetSubscription { let datasourceDataArray: Array = []; datasourceDataArray = datasourceDataArray.concat(datasource.dataKeys.map((dataKey, keyIndex) => { dataKey.hidden = !!dataKey.settings.hideDataByDefault; - dataKey.inLegend = !dataKey.settings.removeFromLegend; + dataKey.inLegend = dataKey.settings.showInLegend || + (isUndefined(dataKey.settings.showInLegend) && !dataKey.settings.removeFromLegend); dataKey.label = this.ctx.utils.customTranslation(dataKey.label, dataKey.label); const datasourceData: DatasourceData = { datasource, diff --git a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts index 0d02fc956f..2089c03d5d 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts +++ b/ui-ngx/src/app/modules/home/components/dashboard-page/add-widget-dialog.component.ts @@ -29,6 +29,7 @@ import { WidgetConfigComponentData, WidgetInfo } from '@home/models/widget-compo import { isDefined, isDefinedAndNotNull, isString } from '@core/utils'; import { TranslateService } from '@ngx-translate/core'; import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; export interface AddWidgetDialogData { dashboard: Dashboard; @@ -97,6 +98,7 @@ export class AddWidgetDialogComponent extends DialogComponent +
+ + + + +
diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts index 0c95324d85..c13eed9c03 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/default-tenant-profile-configuration.component.ts @@ -108,7 +108,9 @@ export class DefaultTenantProfileConfigurationComponent implements ControlValueA wsUpdatesPerSessionRateLimit: [null, []], cassandraQueryTenantRateLimitsConfiguration: [null, []], edgeEventRateLimits: [null, []], - edgeEventRateLimitsPerEdge: [null, []] + edgeEventRateLimitsPerEdge: [null, []], + edgeUplinkMessagesRateLimits: [null, []], + edgeUplinkMessagesRateLimitsPerEdge: [null, []] }); this.defaultTenantProfileConfigurationFormGroup.get('smsEnabled').valueChanges.pipe( diff --git a/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits.models.ts b/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits.models.ts index 8924682aa1..ee13ea9b23 100644 --- a/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits.models.ts +++ b/ui-ngx/src/app/modules/home/components/profile/tenant/rate-limits/rate-limits.models.ts @@ -37,7 +37,9 @@ export enum RateLimitsType { TENANT_NOTIFICATION_REQUEST_RATE_LIMIT = 'TENANT_NOTIFICATION_REQUEST_RATE_LIMIT', TENANT_NOTIFICATION_REQUESTS_PER_RULE_RATE_LIMIT = 'TENANT_NOTIFICATION_REQUESTS_PER_RULE_RATE_LIMIT', EDGE_EVENTS_RATE_LIMIT = 'EDGE_EVENTS_RATE_LIMIT', - EDGE_EVENTS_PER_EDGE_RATE_LIMIT = 'EDGE_EVENTS_PER_EDGE_RATE_LIMIT' + EDGE_EVENTS_PER_EDGE_RATE_LIMIT = 'EDGE_EVENTS_PER_EDGE_RATE_LIMIT', + EDGE_UPLINK_MESSAGES_RATE_LIMIT = 'EDGE_UPLINK_MESSAGES_RATE_LIMIT', + EDGE_UPLINK_MESSAGES_PER_EDGE_RATE_LIMIT = 'EDGE_UPLINK_MESSAGES_PER_EDGE_RATE_LIMIT' } export const rateLimitsLabelTranslationMap = new Map( @@ -58,6 +60,8 @@ export const rateLimitsLabelTranslationMap = new Map( [RateLimitsType.TENANT_NOTIFICATION_REQUESTS_PER_RULE_RATE_LIMIT, 'tenant-profile.tenant-notification-requests-per-rule-rate-limit'], [RateLimitsType.EDGE_EVENTS_RATE_LIMIT, 'tenant-profile.rate-limits.edge-events-rate-limit'], [RateLimitsType.EDGE_EVENTS_PER_EDGE_RATE_LIMIT, 'tenant-profile.rate-limits.edge-events-per-edge-rate-limit'], + [RateLimitsType.EDGE_UPLINK_MESSAGES_RATE_LIMIT, 'tenant-profile.rate-limits.edge-uplink-messages-rate-limit'], + [RateLimitsType.EDGE_UPLINK_MESSAGES_PER_EDGE_RATE_LIMIT, 'tenant-profile.rate-limits.edge-uplink-messages-per-edge-rate-limit'], ] ); @@ -79,6 +83,8 @@ export const rateLimitsDialogTitleTranslationMap = new Map(); - widgetActionFormGroup: UntypedFormGroup; + widgetActionFormGroup: FormGroup; isAdd: boolean; action: WidgetActionDescriptorInfo; @@ -83,7 +85,7 @@ export class WidgetActionDialogComponent extends DialogComponent, - public fb: UntypedFormBuilder) { + public fb: FormBuilder) { super(store, router, dialogRef); this.isAdd = data.isAdd; if (this.isAdd) { @@ -100,19 +102,14 @@ export class WidgetActionDialogComponent extends DialogComponent { + return (c: FormControl) => { const newName = c.value; - const valid = this.checkActionName(newName, this.widgetActionFormGroup.get('actionSourceId').value); + const valid = this.checkActionName(newName, c.parent?.get('actionSourceId').value); return !valid ? { actionNameNotUnique: true } : null; @@ -182,7 +179,7 @@ export class WidgetActionDialogComponent extends DialogComponent +
+ +
+ {{ 'tooltip.show-date-time-interval' | translate }} +
+
+
{{ 'tooltip.background-color' | translate }}
+
+ +
+ {{ 'tooltip.show-date-time-interval' | translate }} +
+
+
{{ 'tooltip.background-color' | translate }}
+ + + + + + + + + + + +
+
widget-config.appearance
+
+ + {{ 'widget-config.title' | translate }} + +
+ + + + + + + +
+
+
+ + {{ 'widget-config.card-icon' | translate }} + +
+ + + + + + + + +
+
+
+
+
widgets.time-series-chart.chart
+
+ + {{ 'widgets.time-series-chart.data-zoom' | translate }} + +
+
+ +
+ {{ 'widgets.time-series-chart.stack-mode' | translate }} +
+
+
+
+
+
widgets.time-series-chart.axis.x-axis
+ + +
+ + +
+ + + + + {{ 'widget-config.legend' | translate }} + + + + +
+
{{ 'legend.label' | translate }}
+
+ + + + +
+
+ + +
+
+
+
+ + + + + {{ 'widget-config.tooltip' | translate }} + + + + +
+
{{ 'tooltip.trigger' | translate }}
+ + {{ 'tooltip.trigger-point' | translate }} + {{ 'tooltip.trigger-axis' | translate }} + +
+
+
{{ 'tooltip.value' | translate }}
+
+ + + + +
+
+
+ + {{ 'tooltip.date' | translate }} + +
+ + + + + +
+
+
+ +
+ {{ 'tooltip.show-date-time-interval' | translate }} +
+
+
+
+
{{ 'tooltip.background-color' | translate }}
+ + +
+
+
{{ 'tooltip.background-blur' | translate }}
+ + +
px
+
+
+
+
+
+ + +
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
widget-config.show-card-buttons
+ + {{ 'fullscreen.fullscreen' | translate }} + +
+
+
{{ 'widget-config.card-border-radius' | translate }}
+ + + +
+
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts new file mode 100644 index 0000000000..31dca75c7e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/chart/time-series-chart-basic-config.component.ts @@ -0,0 +1,367 @@ +/// +/// 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. +/// + +import { Component, Injector } from '@angular/core'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { BasicWidgetConfigComponent } from '@home/components/widget/config/widget-config.component.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { + DataKey, + Datasource, + legendPositions, + legendPositionTranslationMap, + WidgetConfig, +} from '@shared/models/widget.models'; +import { WidgetConfigComponent } from '@home/components/widget/widget-config.component'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { + getTimewindowConfig, + setTimewindowConfig +} from '@home/components/widget/config/timewindow-config-panel.component'; +import { formatValue, isDefinedAndNotNull, isUndefined, mergeDeep } from '@core/utils'; +import { + cssSizeToStrSize, + DateFormatProcessor, + DateFormatSettings, + resolveCssSize +} from '@shared/models/widget-settings.models'; +import { + timeSeriesChartWidgetDefaultSettings, + TimeSeriesChartWidgetSettings +} from '@home/components/widget/lib/chart/time-series-chart-widget.models'; +import { EChartsTooltipTrigger } from '@home/components/widget/lib/chart/echarts-widget.models'; +import { + TimeSeriesChartKeySettings, TimeSeriesChartThreshold, + TimeSeriesChartType, TimeSeriesChartYAxes, + TimeSeriesChartYAxisId +} from '@home/components/widget/lib/chart/time-series-chart.models'; + +@Component({ + selector: 'tb-time-series-chart-basic-config', + templateUrl: './time-series-chart-basic-config.component.html', + styleUrls: ['../basic-config.scss'] +}) +export class TimeSeriesChartBasicConfigComponent extends BasicWidgetConfigComponent { + + public get datasource(): Datasource { + const datasources: Datasource[] = this.timeSeriesChartWidgetConfigForm.get('datasources').value; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + + public get yAxisIds(): TimeSeriesChartYAxisId[] { + const yAxes: TimeSeriesChartYAxes = this.timeSeriesChartWidgetConfigForm.get('yAxes').value; + return Object.keys(yAxes); + } + + TimeSeriesChartType = TimeSeriesChartType; + + EChartsTooltipTrigger = EChartsTooltipTrigger; + + legendPositions = legendPositions; + + legendPositionTranslationMap = legendPositionTranslationMap; + + timeSeriesChartWidgetConfigForm: UntypedFormGroup; + + tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); + + tooltipDatePreviewFn = this._tooltipDatePreviewFn.bind(this); + + chartType: TimeSeriesChartType = TimeSeriesChartType.default; + + constructor(protected store: Store, + protected widgetConfigComponent: WidgetConfigComponent, + private $injector: Injector, + private fb: UntypedFormBuilder) { + super(store, widgetConfigComponent); + } + + public yAxisRemoved(yAxisId: TimeSeriesChartYAxisId): void { + if (this.widgetConfig.config.datasources && this.widgetConfig.config.datasources.length > 1) { + for (let i = 1; i < this.widgetConfig.config.datasources.length; i++) { + const datasource = this.widgetConfig.config.datasources[i]; + this.removeYaxisId(datasource.dataKeys, yAxisId); + } + } + } + + protected configForm(): UntypedFormGroup { + return this.timeSeriesChartWidgetConfigForm; + } + + protected setupConfig(widgetConfig: WidgetConfigComponentData) { + const params = widgetConfig.typeParameters as any; + if (isDefinedAndNotNull(params.chartType)) { + this.chartType = params.chartType; + } + super.setupConfig(widgetConfig); + } + + protected defaultDataKeys(configData: WidgetConfigComponentData): DataKey[] { + return [{ name: 'temperature', label: 'Temperature', type: DataKeyType.timeseries, units: '°C', decimals: 0 }]; + } + + protected onConfigSet(configData: WidgetConfigComponentData) { + const settings: TimeSeriesChartWidgetSettings = mergeDeep({} as TimeSeriesChartWidgetSettings, + timeSeriesChartWidgetDefaultSettings, configData.config.settings as TimeSeriesChartWidgetSettings); + const iconSize = resolveCssSize(configData.config.iconSize); + this.timeSeriesChartWidgetConfigForm = this.fb.group({ + timewindowConfig: [getTimewindowConfig(configData.config), []], + datasources: [configData.config.datasources, []], + + yAxes: [settings.yAxes, []], + series: [this.getSeries(configData.config.datasources), []], + thresholds: [settings.thresholds, []], + + showTitle: [configData.config.showTitle, []], + title: [configData.config.title, []], + titleFont: [configData.config.titleFont, []], + titleColor: [configData.config.titleColor, []], + + showIcon: [configData.config.showTitleIcon, []], + iconSize: [iconSize[0], [Validators.min(0)]], + iconSizeUnit: [iconSize[1], []], + icon: [configData.config.titleIcon, []], + iconColor: [configData.config.iconColor, []], + + dataZoom: [settings.dataZoom, []], + stack: [settings.stack, []], + + xAxis: [settings.xAxis, []], + + noAggregationBarWidthSettings: [settings.noAggregationBarWidthSettings, []], + + showLegend: [settings.showLegend, []], + legendLabelFont: [settings.legendLabelFont, []], + legendLabelColor: [settings.legendLabelColor, []], + legendConfig: [settings.legendConfig, []], + + showTooltip: [settings.showTooltip, []], + tooltipTrigger: [settings.tooltipTrigger, []], + tooltipValueFont: [settings.tooltipValueFont, []], + tooltipValueColor: [settings.tooltipValueColor, []], + tooltipShowDate: [settings.tooltipShowDate, []], + tooltipDateFormat: [settings.tooltipDateFormat, []], + tooltipDateFont: [settings.tooltipDateFont, []], + tooltipDateColor: [settings.tooltipDateColor, []], + tooltipDateInterval: [settings.tooltipDateInterval, []], + + tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], + tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], + + animation: [settings.animation, []], + + background: [settings.background, []], + + cardButtons: [this.getCardButtons(configData.config), []], + borderRadius: [configData.config.borderRadius, []], + padding: [settings.padding, []], + + actions: [configData.config.actions || {}, []] + }); + } + + protected prepareOutputConfig(config: any): WidgetConfigComponentData { + setTimewindowConfig(this.widgetConfig.config, config.timewindowConfig); + this.widgetConfig.config.datasources = config.datasources; + this.setSeries(config.series, this.widgetConfig.config.datasources); + + this.widgetConfig.config.showTitle = config.showTitle; + this.widgetConfig.config.title = config.title; + this.widgetConfig.config.titleFont = config.titleFont; + this.widgetConfig.config.titleColor = config.titleColor; + + this.widgetConfig.config.showTitleIcon = config.showIcon; + this.widgetConfig.config.iconSize = cssSizeToStrSize(config.iconSize, config.iconSizeUnit); + this.widgetConfig.config.titleIcon = config.icon; + this.widgetConfig.config.iconColor = config.iconColor; + + this.widgetConfig.config.settings = this.widgetConfig.config.settings || {}; + + this.widgetConfig.config.settings.thresholds = config.thresholds; + + this.widgetConfig.config.settings.dataZoom = config.dataZoom; + this.widgetConfig.config.settings.stack = config.stack; + + this.widgetConfig.config.settings.yAxes = config.yAxes; + this.widgetConfig.config.settings.xAxis = config.xAxis; + + this.widgetConfig.config.settings.noAggregationBarWidthSettings = config.noAggregationBarWidthSettings; + + this.widgetConfig.config.settings.showLegend = config.showLegend; + this.widgetConfig.config.settings.legendLabelFont = config.legendLabelFont; + this.widgetConfig.config.settings.legendLabelColor = config.legendLabelColor; + this.widgetConfig.config.settings.legendConfig = config.legendConfig; + + this.widgetConfig.config.settings.showTooltip = config.showTooltip; + this.widgetConfig.config.settings.tooltipTrigger = config.tooltipTrigger; + this.widgetConfig.config.settings.tooltipValueFont = config.tooltipValueFont; + this.widgetConfig.config.settings.tooltipValueColor = config.tooltipValueColor; + this.widgetConfig.config.settings.tooltipShowDate = config.tooltipShowDate; + this.widgetConfig.config.settings.tooltipDateFormat = config.tooltipDateFormat; + this.widgetConfig.config.settings.tooltipDateFont = config.tooltipDateFont; + this.widgetConfig.config.settings.tooltipDateColor = config.tooltipDateColor; + this.widgetConfig.config.settings.tooltipDateInterval = config.tooltipDateInterval; + this.widgetConfig.config.settings.tooltipBackgroundColor = config.tooltipBackgroundColor; + this.widgetConfig.config.settings.tooltipBackgroundBlur = config.tooltipBackgroundBlur; + + this.widgetConfig.config.settings.animation = config.animation; + + this.widgetConfig.config.settings.background = config.background; + + this.setCardButtons(config.cardButtons, this.widgetConfig.config); + this.widgetConfig.config.borderRadius = config.borderRadius; + this.widgetConfig.config.settings.padding = config.padding; + + this.widgetConfig.config.actions = config.actions; + return this.widgetConfig; + } + + protected validatorTriggers(): string[] { + return ['showTitle', 'showIcon', 'showLegend', 'showTooltip', 'tooltipShowDate']; + } + + protected updateValidators(emitEvent: boolean, trigger?: string) { + const showTitle: boolean = this.timeSeriesChartWidgetConfigForm.get('showTitle').value; + const showIcon: boolean = this.timeSeriesChartWidgetConfigForm.get('showIcon').value; + const showLegend: boolean = this.timeSeriesChartWidgetConfigForm.get('showLegend').value; + const showTooltip: boolean = this.timeSeriesChartWidgetConfigForm.get('showTooltip').value; + const tooltipShowDate: boolean = this.timeSeriesChartWidgetConfigForm.get('tooltipShowDate').value; + + if (showTitle) { + this.timeSeriesChartWidgetConfigForm.get('title').enable(); + this.timeSeriesChartWidgetConfigForm.get('titleFont').enable(); + this.timeSeriesChartWidgetConfigForm.get('titleColor').enable(); + this.timeSeriesChartWidgetConfigForm.get('showIcon').enable({emitEvent: false}); + if (showIcon) { + this.timeSeriesChartWidgetConfigForm.get('iconSize').enable(); + this.timeSeriesChartWidgetConfigForm.get('iconSizeUnit').enable(); + this.timeSeriesChartWidgetConfigForm.get('icon').enable(); + this.timeSeriesChartWidgetConfigForm.get('iconColor').enable(); + } else { + this.timeSeriesChartWidgetConfigForm.get('iconSize').disable(); + this.timeSeriesChartWidgetConfigForm.get('iconSizeUnit').disable(); + this.timeSeriesChartWidgetConfigForm.get('icon').disable(); + this.timeSeriesChartWidgetConfigForm.get('iconColor').disable(); + } + } else { + this.timeSeriesChartWidgetConfigForm.get('title').disable(); + this.timeSeriesChartWidgetConfigForm.get('titleFont').disable(); + this.timeSeriesChartWidgetConfigForm.get('titleColor').disable(); + this.timeSeriesChartWidgetConfigForm.get('showIcon').disable({emitEvent: false}); + this.timeSeriesChartWidgetConfigForm.get('iconSize').disable(); + this.timeSeriesChartWidgetConfigForm.get('iconSizeUnit').disable(); + this.timeSeriesChartWidgetConfigForm.get('icon').disable(); + this.timeSeriesChartWidgetConfigForm.get('iconColor').disable(); + } + + if (showLegend) { + this.timeSeriesChartWidgetConfigForm.get('legendLabelFont').enable(); + this.timeSeriesChartWidgetConfigForm.get('legendLabelColor').enable(); + this.timeSeriesChartWidgetConfigForm.get('legendConfig').enable(); + } else { + this.timeSeriesChartWidgetConfigForm.get('legendLabelFont').disable(); + this.timeSeriesChartWidgetConfigForm.get('legendLabelColor').disable(); + this.timeSeriesChartWidgetConfigForm.get('legendConfig').disable(); + } + + if (showTooltip) { + this.timeSeriesChartWidgetConfigForm.get('tooltipTrigger').enable(); + this.timeSeriesChartWidgetConfigForm.get('tooltipValueFont').enable(); + this.timeSeriesChartWidgetConfigForm.get('tooltipValueColor').enable(); + this.timeSeriesChartWidgetConfigForm.get('tooltipShowDate').enable({emitEvent: false}); + this.timeSeriesChartWidgetConfigForm.get('tooltipBackgroundColor').enable(); + this.timeSeriesChartWidgetConfigForm.get('tooltipBackgroundBlur').enable(); + if (tooltipShowDate) { + this.timeSeriesChartWidgetConfigForm.get('tooltipDateFormat').enable(); + this.timeSeriesChartWidgetConfigForm.get('tooltipDateFont').enable(); + this.timeSeriesChartWidgetConfigForm.get('tooltipDateColor').enable(); + this.timeSeriesChartWidgetConfigForm.get('tooltipDateInterval').enable(); + } else { + this.timeSeriesChartWidgetConfigForm.get('tooltipDateFormat').disable(); + this.timeSeriesChartWidgetConfigForm.get('tooltipDateFont').disable(); + this.timeSeriesChartWidgetConfigForm.get('tooltipDateColor').disable(); + this.timeSeriesChartWidgetConfigForm.get('tooltipDateInterval').disable(); + } + } else { + this.timeSeriesChartWidgetConfigForm.get('tooltipValueFont').disable(); + this.timeSeriesChartWidgetConfigForm.get('tooltipValueColor').disable(); + this.timeSeriesChartWidgetConfigForm.get('tooltipShowDate').disable({emitEvent: false}); + this.timeSeriesChartWidgetConfigForm.get('tooltipDateFormat').disable(); + this.timeSeriesChartWidgetConfigForm.get('tooltipDateFont').disable(); + this.timeSeriesChartWidgetConfigForm.get('tooltipDateColor').disable(); + this.timeSeriesChartWidgetConfigForm.get('tooltipDateInterval').disable(); + this.timeSeriesChartWidgetConfigForm.get('tooltipBackgroundColor').disable(); + this.timeSeriesChartWidgetConfigForm.get('tooltipBackgroundBlur').disable(); + } + } + + private getSeries(datasources?: Datasource[]): DataKey[] { + if (datasources && datasources.length) { + return datasources[0].dataKeys || []; + } + return []; + } + + private setSeries(series: DataKey[], datasources?: Datasource[]) { + if (datasources && datasources.length) { + datasources[0].dataKeys = series; + } + } + + private removeYaxisId(series: DataKey[], yAxisId: TimeSeriesChartYAxisId): boolean { + let changed = false; + if (series) { + series.forEach(key => { + const keySettings = ((key.settings || {}) as TimeSeriesChartKeySettings); + if (keySettings.yAxisId === yAxisId) { + keySettings.yAxisId = 'default'; + changed = true; + } + }); + } + return changed; + } + + private getCardButtons(config: WidgetConfig): string[] { + const buttons: string[] = []; + if (isUndefined(config.enableFullscreen) || config.enableFullscreen) { + buttons.push('fullscreen'); + } + return buttons; + } + + private setCardButtons(buttons: string[], config: WidgetConfig) { + config.enableFullscreen = buttons.includes('fullscreen'); + } + + private _tooltipValuePreviewFn(): string { + return formatValue(22, 0, '°C', false); + } + + private _tooltipDatePreviewFn(): string { + const dateFormat: DateFormatSettings = this.timeSeriesChartWidgetConfigForm.get('tooltipDateFormat').value; + const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat); + processor.update(Date.now()); + return processor.formatted; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html index 8b233f0b09..0c8be80bf6 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.html @@ -22,126 +22,46 @@ {{ 'datakey.latest' | translate }} - - - -
-
-
- - notifications - - - timeline - -
-
- - - -
-
- - -
-
- -
- - - - - notifications - - - timeline - - - - - -
- - - + + +
+ + + + + {{ timeSeriesChartSeriesTypeIcons.get(keyRowFormGroup.get('timeSeriesType').value) }} + + + + {{ timeSeriesChartSeriesTypeIcons.get(type) }} + {{ timeSeriesChartSeriesTypeTranslations.get(type) | translate }} + + + +
+
+ + + + {{ yAxis }} + + + +
@@ -178,19 +98,3 @@
- - - f() - - - - - - - - {{ modelValue?.aggregationType }}({{ modelValue?.name }}) - - - {{modelValue?.name}} - - diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.scss b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.scss index 06b740a8fb..01e042e0a8 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.scss @@ -16,37 +16,13 @@ @import '../../../../../../../../scss/constants'; .tb-data-key-row { - .mat-mdc-form-field.tb-inline-field.tb-key-field { - .mat-mdc-text-field-wrapper:not(.mdc-text-field--outlined) { - padding-left: 8px; - padding-right: 0; - .mat-mdc-form-field-infix { - padding-top: 0; - padding-bottom: 6px; - .mdc-evolution-chip-set .mdc-evolution-chip { - margin: 0; - } - input.mat-mdc-chip-input { - height: 32px; - margin-left: 0; - } - } - } - .mat-mdc-chip.mat-mdc-standard-chip.tb-datakey-chip { - .tb-attribute-chip { - .tb-chip-labels { - background: transparent; - } - } - } - } .tb-source-field { width: 108px; min-width: 108px; } - .tb-key-field { + .tb-data-key-input { flex: 1; min-width: 150px; @media #{$mat-gt-sm} { @@ -59,7 +35,7 @@ min-width: 100px; } - .tb-color-field, .tb-units-field, .tb-decimals-field { + .tb-color-field, .tb-units-field, .tb-decimals-field, .tb-time-series-type-field { display: flex; flex-direction: row; place-content: center; @@ -81,10 +57,41 @@ min-width: 60px; } + .tb-y-axis-field { + width: 100px; + min-width: 100px; + .mat-mdc-form-field { + width: 100%; + } + } + + .tb-time-series-type-field { + width: 60px; + min-width: 60px; + .mat-icon { + color: rgba(0,0,0,0.76); + } + } + .tb-label-field, .tb-units-field, .tb-color-field, .tb-decimals-field { display: none; @media #{$mat-gt-sm} { - display: block; + display: flex; + } + } + + .tb-y-axis-field { + @media #{$mat-md} { + display: none; + } + } + + .tb-time-series-type-field { + @media #{$mat-xs} { + display: none; + } + @media #{$mat-md} { + display: none; } } @@ -113,3 +120,11 @@ } } } + +.tb-time-series-type-field { + .mat-mdc-option:not(.mdc-list-item--selected) { + .mat-icon { + color: rgba(0,0,0,0.54); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts index b75b9c09a4..72eedc21a0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-key-row.component.ts @@ -17,15 +17,11 @@ import { ChangeDetectorRef, Component, - ElementRef, EventEmitter, forwardRef, - Input, - OnChanges, + Input, OnChanges, OnInit, - Output, - SimpleChanges, - ViewChild, + Output, SimpleChanges, ViewEncapsulation } from '@angular/core'; import { @@ -49,15 +45,8 @@ import { widgetType } from '@shared/models/widget.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; -import { AggregationType } from '@shared/models/time/time.models'; -import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; -import { MatChipGrid, MatChipInputEvent } from '@angular/material/chips'; -import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; -import { MatAutocomplete, MatAutocompleteTrigger } from '@angular/material/autocomplete'; -import { Observable, of } from 'rxjs'; -import { filter, map, mergeMap, publishReplay, refCount, share, tap } from 'rxjs/operators'; -import { TranslateService } from '@ngx-translate/core'; -import { TruncatePipe } from '@shared/pipe/truncate.pipe'; +import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; +import { merge } from 'rxjs'; import { DataKeyConfigDialogComponent, DataKeyConfigDialogData @@ -66,8 +55,13 @@ import { deepClone } from '@core/utils'; import { Dashboard } from '@shared/models/dashboard.models'; import { IAliasController } from '@core/api/widget-api.models'; import { coerceBoolean } from '@shared/decorators/coercion'; -import { alarmFields } from '@shared/models/alarm.models'; -import { UtilsService } from '@core/services/utils.service'; +import { + TimeSeriesChartKeySettings, + TimeSeriesChartSeriesType, + timeSeriesChartSeriesTypeIcons, + timeSeriesChartSeriesTypes, + timeSeriesChartSeriesTypeTranslations, TimeSeriesChartYAxisId +} from '@home/components/widget/lib/chart/time-series-chart.models'; export const dataKeyValid = (key: DataKey): boolean => !!key && !!key.type && !!key.name; @@ -96,15 +90,9 @@ export const dataKeyRowValidator = (control: AbstractControl): ValidationErrors }) export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChanges { - dataKeyTypes = DataKeyType; - widgetTypes = widgetType; - - separatorKeysCodes: number[] = [ENTER, COMMA, SEMICOLON]; - - @ViewChild('keyInput') keyInput: ElementRef; - @ViewChild('keyAutocomplete') matAutocomplete: MatAutocomplete; - @ViewChild(MatAutocompleteTrigger) autocomplete: MatAutocompleteTrigger; - @ViewChild('chipList') chipList: MatChipGrid; + timeSeriesChartSeriesTypes = timeSeriesChartSeriesTypes; + timeSeriesChartSeriesTypeTranslations = timeSeriesChartSeriesTypeTranslations; + timeSeriesChartSeriesTypeIcons = timeSeriesChartSeriesTypeIcons; @Input() disabled: boolean; @@ -150,6 +138,17 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan @coerceBoolean() hideDecimals = false; + @Input() + @coerceBoolean() + timeSeriesChart = false; + + @Input() + @coerceBoolean() + showTimeSeriesType = false; + + @Input() + yAxisIds: TimeSeriesChartYAxisId[]; + @Input() @coerceBoolean() singleRow = true; @@ -166,23 +165,13 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan @Output() keyRemoved = new EventEmitter(); - keysFormControl: UntypedFormControl; - keyFormControl: UntypedFormControl; keyRowFormGroup: UntypedFormGroup; modelValue: DataKey; - filteredKeys: Observable>; - - keySearchText = ''; - - alarmKeys: Array; - functionTypeKeys: Array; - - private latestKeySearchTextResult: Array = null; - private keyFetchObservable$: Observable> = null; + generateDataKey = this._generateDataKey.bind(this); get widgetType(): widgetType { return this.widgetConfigComponent.widgetType; @@ -220,6 +209,10 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan return this.widgetConfigComponent.modelValue?.latestDataKeySettingsDirective; } + get dataKeySettingsFunction(): DataKeySettingsFunction { + return this.widgetConfigComponent.modelValue?.dataKeySettingsFunction; + } + get isEntityDatasource(): boolean { return [DatasourceType.device, DatasourceType.entity].includes(this.datasourceType); } @@ -237,29 +230,11 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan constructor(private fb: UntypedFormBuilder, private dialog: MatDialog, private cd: ChangeDetectorRef, - public translate: TranslateService, - public truncate: TruncatePipe, - private utils: UtilsService, private widgetConfigComponent: WidgetConfigComponent) { } ngOnInit() { - this.alarmKeys = []; - for (const name of Object.keys(alarmFields)) { - this.alarmKeys.push({ - name, - type: DataKeyType.alarm - }); - } - this.functionTypeKeys = []; - for (const type of this.utils.getPredefinedFunctionsList()) { - this.functionTypeKeys.push({ - name: type, - type: DataKeyType.function - }); - } - this.keyFormControl = this.fb.control(''); - this.keysFormControl = this.fb.control([], this.required ? [Validators.required] : []); + this.keyFormControl = this.fb.control(null, this.required ? [Validators.required] : []); this.keyRowFormGroup = this.fb.group({ label: [null, []], color: [null, []], @@ -268,52 +243,24 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan }); if (this.hasAdditionalLatestDataKeys) { this.keyRowFormGroup.addControl('latest', this.fb.control(false)); - this.keyRowFormGroup.valueChanges.subscribe( - () => this.clearKeySearchCache() - ); } - this.keyRowFormGroup.valueChanges.subscribe( + if (this.timeSeriesChart) { + this.keyRowFormGroup.addControl('yAxis', this.fb.control(null)); + this.keyRowFormGroup.addControl('timeSeriesType', this.fb.control(null)); + } + merge(this.keyFormControl.valueChanges, this.keyRowFormGroup.valueChanges).subscribe( () => this.updateModel() ); - this.filteredKeys = this.keyFormControl.valueChanges - .pipe( - tap((value: string | DataKey) => { - if (value && typeof value !== 'string') { - this.addKeyFromChipValue(value); - } else if (value === null) { - this.clearKeyChip(this.keyInput.nativeElement.value); - } - }), - filter((value) => typeof value === 'string'), - map((value) => value ? (typeof value === 'string' ? value : value.name) : ''), - mergeMap(name => this.fetchKeys(name) ), - share() - ); - } - - private reset() { - if (this.keyInput) { - this.keyInput.nativeElement.value = ''; - } - this.keyFormControl.patchValue('', {emitEvent: false}); - this.latestKeySearchTextResult = null; } ngOnChanges(changes: SimpleChanges): void { for (const propName of Object.keys(changes)) { const change = changes[propName]; if (!change.firstChange && change.currentValue !== change.previousValue) { - if (['deviceId', 'entityAliasId'].includes(propName)) { - this.clearKeySearchCache(); - } else if (['datasourceType'].includes(propName)) { - if ([DatasourceType.device, DatasourceType.entity].includes(change.previousValue) && - [DatasourceType.device, DatasourceType.entity].includes(change.currentValue)) { - this.clearKeySearchCache(); - } else { - this.clearKeySearchCache(); - setTimeout(() => { - this.reset(); - }, 1); + if (this.timeSeriesChart && ['yAxisIds'].includes(propName)) { + if (this.modelValue?.settings?.yAxisId && + !this.yAxisIds.includes(this.modelValue.settings.yAxisId)) { + this.keyRowFormGroup.patchValue({yAxis: 'default'}, {emitEvent: true}); } } } @@ -330,10 +277,10 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan setDisabledState(isDisabled: boolean): void { this.disabled = isDisabled; if (isDisabled) { - this.keysFormControl.disable({emitEvent: false}); + this.keyFormControl.disable({emitEvent: false}); this.keyRowFormGroup.disable({emitEvent: false}); } else { - this.keysFormControl.enable({emitEvent: false}); + this.keyFormControl.enable({emitEvent: false}); this.keyRowFormGroup.enable({emitEvent: false}); } } @@ -353,34 +300,17 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan latest: (value as any)?.latest }, {emitEvent: false}); } - this.keysFormControl.patchValue(this.modelValue ? [this.modelValue] : [], {emitEvent: false}); - this.cd.markForCheck(); - } - - dataKeyHasAggregation(): boolean { - return this.widgetConfigComponent.widgetType === widgetType.latest && this.modelValue?.type === DataKeyType.timeseries - && this.modelValue?.aggregationType && this.modelValue?.aggregationType !== AggregationType.NONE; - } - - dataKeyHasPostprocessing(): boolean { - return !!this.modelValue?.postFuncBody; - } - - displayKeyFn(key?: DataKey): string | undefined { - return key ? key.name : undefined; - } - - createKey(name: string, dataKeyType: DataKeyType = this.dataKeyType) { - this.addKeyFromChipValue({name: name ? name.trim() : '', type: dataKeyType}); - } - - addKey(event: MatChipInputEvent): void { - const value = event.value; - if ((value || '').trim() && this.dataKeyType) { - this.addKeyFromChipValue({name: value.trim(), type: this.dataKeyType}); - } else { - this.clearKeyChip(); + if (this.timeSeriesChart) { + const settings = value?.settings as TimeSeriesChartKeySettings; + const yAxis = settings?.yAxisId || 'default'; + const timeSeriesType = settings?.type || TimeSeriesChartSeriesType.line; + this.keyRowFormGroup.patchValue({ + yAxis, + timeSeriesType + }, {emitEvent: false}); } + this.keyFormControl.patchValue(deepClone(this.modelValue), {emitEvent: false}); + this.cd.markForCheck(); } editKey(advanced = false) { @@ -413,109 +343,41 @@ export class DataKeyRowComponent implements ControlValueAccessor, OnInit, OnChan this.keyRowFormGroup.get('color').patchValue(this.modelValue.color, {emitEvent: false}); this.keyRowFormGroup.get('units').patchValue(this.modelValue.units, {emitEvent: false}); this.keyRowFormGroup.get('decimals').patchValue(this.modelValue.decimals, {emitEvent: false}); + if (this.timeSeriesChart) { + this.keyRowFormGroup.get('yAxis').patchValue(this.modelValue.settings?.yAxisId, {emitEvent: false}); + this.keyRowFormGroup.get('timeSeriesType').patchValue(this.modelValue.settings?.type, {emitEvent: false}); + } + this.keyFormControl.patchValue(deepClone(this.modelValue), {emitEvent: false}); this.updateModel(); this.cd.markForCheck(); } }); } - removeKey() { - this.modelValue = null; - this.updateModel(); - this.clearKeyChip(); - } - - textIsNotEmpty(text: string): boolean { - return text && text.length > 0; - } - - clearKeyChip(value: string = '', focus = true) { - this.autocomplete.closePanel(); - this.keyInput.nativeElement.value = value; - this.keyFormControl.patchValue(value, {emitEvent: focus}); - if (focus) { - setTimeout(() => { - this.keyInput.nativeElement.blur(); - this.keyInput.nativeElement.focus(); - }, 0); - } - } - - onKeyInputFocus() { - if (!this.modelValue?.type) { - this.keyFormControl.updateValueAndValidity({onlySelf: true, emitEvent: true}); - } - } - - private fetchKeys(searchText?: string): Observable> { - if (this.keySearchText !== searchText || this.latestKeySearchTextResult === null) { - this.keySearchText = searchText; - const dataKeyFilter = this.createDataKeyFilter(this.keySearchText); - return this.getKeys().pipe( - map(name => name.filter(dataKeyFilter)), - tap(res => this.latestKeySearchTextResult = res) - ); - } - return of(this.latestKeySearchTextResult); - } - - private getKeys(): Observable> { - if (this.keyFetchObservable$ === null) { - let fetchObservable: Observable>; - if (this.datasourceType === DatasourceType.function) { - const targetKeysList = this.widgetType === widgetType.alarm ? this.alarmKeys : this.functionTypeKeys; - fetchObservable = of(targetKeysList); - } else if (this.datasourceType === DatasourceType.entity && this.entityAliasId || - this.datasourceType === DatasourceType.device && this.deviceId) { - const dataKeyTypes = [DataKeyType.timeseries]; - if (this.isLatestDataKeys || this.widgetType === widgetType.latest || this.widgetType === widgetType.alarm) { - dataKeyTypes.push(DataKeyType.attribute); - dataKeyTypes.push(DataKeyType.entityField); - if (this.widgetType === widgetType.alarm) { - dataKeyTypes.push(DataKeyType.alarm); - } - } - if (this.datasourceType === DatasourceType.device) { - fetchObservable = this.callbacks.fetchEntityKeysForDevice(this.deviceId, dataKeyTypes); - } else { - fetchObservable = this.callbacks.fetchEntityKeys(this.entityAliasId, dataKeyTypes); - } - } else { - fetchObservable = of([]); - } - this.keyFetchObservable$ = fetchObservable.pipe( - publishReplay(1), - refCount() - ); - } - return this.keyFetchObservable$; - } - - private createDataKeyFilter(query: string): (key: DataKey) => boolean { - const lowercaseQuery = query.toLowerCase(); - return key => key.name.toLowerCase().startsWith(lowercaseQuery); - } - - private addKeyFromChipValue(chip: DataKey) { - this.modelValue = this.callbacks.generateDataKey(chip.name, chip.type, this.dataKeySettingsSchema); + private _generateDataKey(key: DataKey): DataKey { + key = this.callbacks.generateDataKey(key.name, key.type, this.dataKeySettingsSchema, this.isLatestDataKeys, + this.dataKeySettingsFunction); if (!this.keyRowFormGroup.get('label').value) { - this.keyRowFormGroup.get('label').patchValue(this.modelValue.label, {emitEvent: false}); + this.keyRowFormGroup.get('label').patchValue(key.label, {emitEvent: false}); } - this.updateModel(); - this.clearKeyChip('', false); - } - - private clearKeySearchCache() { - this.keySearchText = ''; - this.keyFetchObservable$ = null; - this.latestKeySearchTextResult = null; + if (this.timeSeriesChart) { + this.keyRowFormGroup.get('yAxis').patchValue(key.settings?.yAxisId, {emitEvent: false}); + this.keyRowFormGroup.get('timeSeriesType').patchValue(key.settings?.type, {emitEvent: false}); + } + return key; } private updateModel() { - this.keysFormControl.patchValue(this.modelValue ? [this.modelValue] : [], {emitEvent: false}); + this.modelValue = this.keyFormControl.value; if (this.modelValue !== null) { const value: DataKey = this.keyRowFormGroup.value; this.modelValue = {...this.modelValue, ...value}; + if (this.timeSeriesChart) { + this.modelValue.settings.yAxisId = (this.modelValue as any).yAxis; + this.modelValue.settings.type = (this.modelValue as any).timeSeriesType; + delete (this.modelValue as any).yAxis; + delete (this.modelValue as any).timeSeriesType; + } } this.propagateChange(this.modelValue); } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.html index 2771229326..f140bd7445 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.html @@ -15,13 +15,15 @@ limitations under the License. --> -
+
{{ panelTitle }}
datakey.source
datakey.key
datakey.label
+
widgets.time-series-chart.series.type
+
widgets.time-series-chart.axis.y-axis
datakey.color
widget-config.units-short
widget-config.decimals-short
@@ -43,6 +45,9 @@ [hasAdditionalLatestDataKeys]="hasAdditionalLatestDataKeys" [hideDataKeyColor]="hideDataKeyColor" [hideDecimals]="hideDecimals" + [timeSeriesChart]="timeSeriesChart" + [showTimeSeriesType]="showTimeSeriesType" + [yAxisIds]="yAxisIds" [hideUnits]="hideUnits" [hideDataKeyDecimals]="hideDataKeyDecimals" [hideDataKeyUnits]="hideDataKeyUnits" diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.scss index 1e4960c40c..a3862c1c58 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.scss @@ -15,52 +15,86 @@ */ @import '../../../../../../../../scss/constants'; -.tb-form-table-header-cell { - &.tb-source-header { - width: 108px; - min-width: 108px; - } - &.tb-key-header { - flex: 1; - min-width: 150px; - @media #{$mat-gt-sm} { - flex: 1 1 60%; +.tb-data-keys-panel { + .tb-form-table-header-cell { + &.tb-source-header { + width: 108px; + min-width: 108px; } - } - &.tb-label-header { - flex: 1 1 40%; - min-width: 100px; - } - &.tb-units-header { - width: 80px; - min-width: 80px; - } - &.tb-color-header { - width: 40px; - min-width: 40px; - } - &.tb-decimals-header { - width: 60px; - min-width: 60px; - } - &.tb-actions-header { - width: 40px; - min-width: 40px; - @media #{$mat-gt-md} { - width: 120px; - min-width: 120px; + + &.tb-key-header { + flex: 1; + min-width: 150px; + @media #{$mat-gt-sm} { + flex: 1 1 60%; + } } - } - &.tb-label-header, &.tb-units-header, &.tb-color-header, &.tb-decimals-header { - display: none; - @media #{$mat-gt-sm} { - display: block; + + &.tb-label-header { + flex: 1 1 40%; + min-width: 100px; + } + + &.tb-units-header { + width: 80px; + min-width: 80px; + } + + &.tb-color-header { + width: 40px; + min-width: 40px; + } + + &.tb-decimals-header { + width: 60px; + min-width: 60px; + } + + &.tb-y-axis-header { + width: 100px; + min-width: 100px; + } + + &.tb-time-series-type-header { + width: 60px; + min-width: 60px; + } + + &.tb-actions-header { + width: 40px; + min-width: 40px; + @media #{$mat-gt-md} { + width: 120px; + min-width: 120px; + } + } + + &.tb-label-header, &.tb-units-header, &.tb-color-header, &.tb-decimals-header { + display: none; + @media #{$mat-gt-sm} { + display: block; + } + } + + &.tb-y-axis-header { + @media #{$mat-md} { + display: none; + } + } + + &.tb-time-series-type-header { + @media #{$mat-xs} { + display: none; + } + @media #{$mat-md} { + display: none; + } } } -} -.tb-form-table-body { - tb-data-key-row { - overflow: hidden; + .tb-form-table-body { + tb-data-key-row { + overflow: hidden; + } } } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts index 73a3b8027c..86367771de 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/basic/common/data-keys-panel.component.ts @@ -42,8 +42,9 @@ import { dataKeyRowValidator, dataKeyValid } from '@home/components/widget/confi import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { UtilsService } from '@core/services/utils.service'; -import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { TimeSeriesChartYAxisId } from '@home/components/widget/lib/chart/time-series-chart.models'; @Component({ selector: 'tb-data-keys-panel', @@ -119,6 +120,17 @@ export class DataKeysPanelComponent implements ControlValueAccessor, OnInit, OnC @coerceBoolean() hideSourceSelection = false; + @Input() + @coerceBoolean() + timeSeriesChart = false; + + @Input() + @coerceBoolean() + showTimeSeriesType = false; + + @Input() + yAxisIds: TimeSeriesChartYAxisId[]; + dataKeyType: DataKeyType; keysListFormGroup: UntypedFormGroup; @@ -142,6 +154,10 @@ export class DataKeysPanelComponent implements ControlValueAccessor, OnInit, OnC return this.widgetConfigComponent.modelValue?.dataKeySettingsSchema; } + get dataKeySettingsFunction(): DataKeySettingsFunction { + return this.widgetConfigComponent.modelValue?.dataKeySettingsFunction; + } + get dragEnabled(): boolean { return this.keysFormArray().controls.length > 1; } @@ -256,7 +272,8 @@ export class DataKeysPanelComponent implements ControlValueAccessor, OnInit, OnC } addKey() { - const dataKey = this.callbacks.generateDataKey('', null, this.datakeySettingsSchema); + const dataKey = this.callbacks.generateDataKey('', null, this.datakeySettingsSchema, + false, this.dataKeySettingsFunction); dataKey.label = ''; dataKey.decimals = 0; if (this.hasAdditionalLatestDataKeys) { diff --git a/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.html b/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.html index fe6c347649..f472325d8d 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.html @@ -196,7 +196,9 @@
diff --git a/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.ts index b120a60809..9ebe8821cf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/data-key-config.component.ts @@ -40,7 +40,7 @@ import { UtilsService } from '@core/services/utils.service'; import { TranslateService } from '@ngx-translate/core'; import { MatDialog } from '@angular/material/dialog'; import { EntityService } from '@core/http/entity.service'; -import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { Observable, of } from 'rxjs'; import { map, mergeMap, publishReplay, refCount, tap } from 'rxjs/operators'; @@ -51,8 +51,10 @@ import { WidgetService } from '@core/http/widget.service'; import { Dashboard } from '@shared/models/dashboard.models'; import { IAliasController } from '@core/api/widget-api.models'; import { aggregationTranslations, AggregationType, ComparisonDuration } from '@shared/models/time/time.models'; -import { genNextLabel } from '@core/utils'; +import { genNextLabel, isDefinedAndNotNull } from '@core/utils'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { WidgetComponentService } from '@home/components/widget/widget-component.service'; @Component({ selector: 'tb-data-key-config', @@ -155,6 +157,8 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con modelValue: DataKey; + widgetConfig: WidgetConfigComponentData; + private propagateChange = null; public dataKeyFormGroup: UntypedFormGroup; @@ -180,12 +184,31 @@ export class DataKeyConfigComponent extends PageComponent implements OnInit, Con private dialog: MatDialog, private translate: TranslateService, private widgetService: WidgetService, + private widgetComponentService: WidgetComponentService, private fb: UntypedFormBuilder) { super(store); this.functionScopeVariables = this.widgetService.getWidgetScopeVariables(); } ngOnInit(): void { + + const widgetInfo = this.widgetComponentService.getInstantWidgetInfo(this.widget); + const typeParameters = widgetInfo.typeParameters; + const dataKeySettingsFunction: DataKeySettingsFunction = typeParameters?.dataKeySettingsFunction; + + this.widgetConfig = { + widgetName: widgetInfo.widgetName, + config: this.widget.config, + widgetType: this.widget.type, + typeParameters, + dataKeySettingsFunction, + settingsDirective: widgetInfo.settingsDirective, + dataKeySettingsDirective: widgetInfo.dataKeySettingsDirective, + latestDataKeySettingsDirective: widgetInfo.latestDataKeySettingsDirective, + hasBasicMode: isDefinedAndNotNull(widgetInfo.hasBasicMode) ? widgetInfo.hasBasicMode : false, + basicModeDirective: widgetInfo.basicModeDirective + } as WidgetConfigComponentData; + this.alarmKeys = []; for (const name of Object.keys(alarmFields)) { this.alarmKeys.push({ diff --git a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.models.ts b/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.models.ts index 893631f57b..b0e80973f0 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.models.ts @@ -18,8 +18,11 @@ import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { DataKey, JsonSettingsSchema } from '@shared/models/widget.models'; import { Observable } from 'rxjs'; +export type DataKeySettingsFunction = (key: DataKey, isLatestDataKey: boolean) => any; + export interface DataKeysCallbacks { - generateDataKey: (chip: any, type: DataKeyType, datakeySettingsSchema: JsonSettingsSchema) => DataKey; + generateDataKey: (chip: any, type: DataKeyType, datakeySettingsSchema: JsonSettingsSchema, + isLatestDataKey: boolean, dataKeySettingsFunction: DataKeySettingsFunction) => DataKey; fetchEntityKeys: (entityAliasId: string, types: Array) => Observable>; fetchEntityKeysForDevice: (deviceId: string, types: Array) => Observable>; } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts index 800c831b67..8c2df629de 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/data-keys.component.ts @@ -52,7 +52,7 @@ import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; import { DataKey, DatasourceType, JsonSettingsSchema, Widget, widgetType } from '@shared/models/widget.models'; import { IAliasController } from '@core/api/widget-api.models'; -import { DataKeysCallbacks } from './data-keys.component.models'; +import { DataKeysCallbacks, DataKeySettingsFunction } from './data-keys.component.models'; import { alarmFields } from '@shared/models/alarm.models'; import { UtilsService } from '@core/services/utils.service'; import { ErrorStateMatcher } from '@angular/material/core'; @@ -139,6 +139,10 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange @Input() optDataKeys: boolean; + @Input() + @coerceBoolean() + latestDataKeys = false; + @Input() @coerceBoolean() simpleDataKeysLabel = false; @@ -149,6 +153,9 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange @Input() datakeySettingsSchema: JsonSettingsSchema; + @Input() + datakeySettingsFunction: DataKeySettingsFunction; + @Input() dataKeySettingsDirective: string; @@ -361,7 +368,8 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange if (this.widgetType === widgetType.alarm) { this.keys = this.utils.getDefaultAlarmDataKeys(); } else if (this.isCountDatasource) { - this.keys = [this.callbacks.generateDataKey('count', DataKeyType.count, this.datakeySettingsSchema)]; + this.keys = [this.callbacks.generateDataKey('count', DataKeyType.count, this.datakeySettingsSchema, + this.latestDataKeys, this.datakeySettingsFunction)]; } else { this.keys = []; } @@ -447,7 +455,8 @@ export class DataKeysComponent implements ControlValueAccessor, OnInit, OnChange } private addFromChipValue(chip: DataKey) { - const key = this.callbacks.generateDataKey(chip.name, chip.type, this.datakeySettingsSchema); + const key = this.callbacks.generateDataKey(chip.name, chip.type, this.datakeySettingsSchema, this.latestDataKeys, + this.datakeySettingsFunction); this.addKey(key); } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html index b72f9f0e5d..13a52a295b 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/config/datasource.component.html @@ -72,6 +72,7 @@ [aliasController]="aliasController" [datakeySettingsSchema]="dataKeySettingsSchema" [dataKeySettingsDirective]="dataKeySettingsDirective" + [datakeySettingsFunction]="dataKeySettingsFunction" [dashboard]="dashboard" [widget]="widget" [callbacks]="dataKeysCallbacks" @@ -82,10 +83,12 @@ { - const dataKey = this.constructDataKey(configData, key); + const dataKey = this.constructDataKey(configData, key, false); dataKeys.push(dataKey); }); } if (latestKeys && latestKeys.length) { latestDataKeys.length = 0; latestKeys.forEach(key => { - const dataKey = this.constructDataKey(configData, key); + const dataKey = this.constructDataKey(configData, key, true); latestDataKeys.push(dataKey); }); } } - protected constructDataKey(configData: WidgetConfigComponentData, key: DataKey): DataKey { + protected constructDataKey(configData: WidgetConfigComponentData, key: DataKey, isLatestKey: boolean): DataKey { const dataKey = - this.widgetConfigComponent.widgetConfigCallbacks.generateDataKey(key.name, key.type, configData.dataKeySettingsSchema); + this.widgetConfigComponent.widgetConfigCallbacks.generateDataKey(key.name, key.type, + configData.dataKeySettingsSchema, isLatestKey, configData.dataKeySettingsFunction); if (key.label) { dataKey.label = key.label; } diff --git a/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts index a4a2e9cc7e..8636db1f77 100644 --- a/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/config/widget-settings.component.ts @@ -49,6 +49,7 @@ import { Dashboard } from '@shared/models/dashboard.models'; import { WidgetService } from '@core/http/widget.service'; import { IAliasController } from '@core/api/widget-api.models'; import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; @Component({ selector: 'tb-widget-settings', @@ -77,6 +78,9 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, On @Input() aliasController: IAliasController; + @Input() + dataKeyCallbacks: DataKeysCallbacks; + @Input() dashboard: Dashboard; @@ -98,7 +102,7 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, On private definedSettingsComponent: IWidgetSettingsComponent; private widgetSettingsFormData: JsonFormComponentData; - private propagateChange = (v: any) => { }; + private propagateChange = (_v: any) => { }; constructor(private translate: TranslateService, private cfr: ComponentFactoryResolver, @@ -138,6 +142,11 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, On this.definedSettingsComponent.aliasController = this.aliasController; } } + if (propName === 'dataKeyCallbacks') { + if (this.definedSettingsComponent) { + this.definedSettingsComponent.dataKeyCallbacks = this.dataKeyCallbacks; + } + } if (propName === 'widgetConfig') { if (this.definedSettingsComponent) { this.definedSettingsComponent.widgetConfig = this.widgetConfig; @@ -225,6 +234,7 @@ export class WidgetSettingsComponent implements ControlValueAccessor, OnInit, On this.definedSettingsComponentRef = this.definedSettingsContainer.createComponent(factory); this.definedSettingsComponent = this.definedSettingsComponentRef.instance; this.definedSettingsComponent.aliasController = this.aliasController; + this.definedSettingsComponent.dataKeyCallbacks = this.dataKeyCallbacks; this.definedSettingsComponent.dashboard = this.dashboard; this.definedSettingsComponent.widget = this.widget; this.definedSettingsComponent.widgetConfig = this.widgetConfig; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.scss index 65be3c924f..8304400818 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.scss @@ -92,6 +92,7 @@ .tb-aggregated-value-card-chart-container { position: relative; flex: 1; + min-width: 0; margin-top: 8px; margin-bottom: 8px; .tb-aggregated-value-card-chart-element { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts index 25be87451e..852f4950ce 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/aggregated-value-card-widget.component.ts @@ -43,16 +43,23 @@ import { getDataKey, getLatestSingleTsValue, overlayStyle, + simpleDateFormat, textStyle } from '@shared/models/widget-settings.models'; -import { TbFlot } from '@home/components/widget/lib/flot-widget'; -import { TbFlotKeySettings, TbFlotSettings } from '@home/components/widget/lib/flot-widget.models'; import { DataKey } from '@shared/models/widget.models'; import { formatNumberValue, formatValue, isDefined, isDefinedAndNotNull, isNumeric } from '@core/utils'; import { map } from 'rxjs/operators'; import { ResizeObserver } from '@juggle/resize-observer'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; +import { TbTimeSeriesChart } from '@home/components/widget/lib/chart/time-series-chart'; +import { + TimeSeriesChartAxisSettings, + TimeSeriesChartKeySettings, + TimeSeriesChartSeriesType, + TimeSeriesChartSettings, TimeSeriesChartYAxisSettings +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { DeepPartial } from '@shared/models/common'; const valuesLayoutHeight = 66; const valuesLayoutVerticalPadding = 16; @@ -101,8 +108,8 @@ export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit backgroundStyle$: Observable; overlayStyle: ComponentStyle = {}; - private flot: TbFlot; - private flotDataKey: DataKey; + private lineChart: TbTimeSeriesChart; + private lineChartDataKey: DataKey; private lastUpdateTs: number; @@ -141,12 +148,18 @@ export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit this.showChart = this.settings.showChart; if (this.showChart) { if (this.ctx.defaultSubscription.firstDatasource?.dataKeys?.length) { - this.flotDataKey = this.ctx.defaultSubscription.firstDatasource?.dataKeys[0]; - this.flotDataKey.settings = { - fillLines: false, - showLines: true, - lineWidth: 2 - } as TbFlotKeySettings; + this.lineChartDataKey = this.ctx.defaultSubscription.firstDatasource?.dataKeys[0]; + this.lineChartDataKey.settings = { + type: TimeSeriesChartSeriesType.line, + lineSettings: { + showLine: true, + step: false, + smooth: false, + lineWidth: 2, + showPoints: false, + showPointLabel: false + } + } as TimeSeriesChartKeySettings; } } @@ -161,33 +174,36 @@ export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit ngAfterViewInit(): void { if (this.showChart && this.ctx.datasources?.length) { - const settings = { - shadowSize: 0, - enableSelection: false, - smoothLines: false, - grid: { - tickColor: 'rgba(0,0,0,0.12)', - horizontalLines: true, - verticalLines: false, - outlineWidth: 0, - minBorderMargin: 0, - margin: 0 - }, - yaxis: { - showLabels: false, - tickGenerator: 'return [(axis.max + axis.min) / 2];' - }, - xaxis: { - showLabels: false - } - } as TbFlotSettings; - this.flot = new TbFlot(this.ctx, 'line', $(this.chartElement.nativeElement), settings); - this.tickMin$ = this.flot.yMin$.pipe( - map((value) => formatValue(value, (this.flotDataKey?.decimals || this.ctx.decimals)) + const settings: DeepPartial = { + dataZoom: false, + xAxis: { + show: false + }, + yAxes: { + default: { + show: true, + showLine: false, + showTicks: false, + showTickLabels: false, + showSplitLines: true, + min: 'dataMin', + max: 'dataMax', + intervalCalculator: + 'var scale = axis.scale; return !scale.isBlank() ? ((scale.getExtent()[1] - scale.getExtent()[0]) / 2) : undefined;' + } + }, + tooltipDateInterval: false, + tooltipDateFormat: simpleDateFormat('dd MMM yyyy HH:mm:ss') + }; + + this.lineChart = new TbTimeSeriesChart(this.ctx, settings, this.chartElement.nativeElement, this.renderer, true); + + this.tickMin$ =this.lineChart.yMin$.pipe( + map((value) => formatValue(value, (this.lineChartDataKey?.decimals || this.ctx.decimals)) + )); + this.tickMax$ = this.lineChart.yMax$.pipe( + map((value) => formatValue(value, (this.lineChartDataKey?.decimals || this.ctx.decimals)) )); - this.tickMax$ = this.flot.yMax$.pipe( - map((value) => formatValue(value, (this.flotDataKey?.decimals || this.ctx.decimals)) - )); } if (this.settings.autoScale && this.showValues) { this.renderer.setStyle(this.valueCardValueContainer.nativeElement, 'height', valuesLayoutHeight + 'px'); @@ -221,7 +237,7 @@ export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit } if (this.showChart) { - this.flot.update(); + this.lineChart.update(); } this.updateLastUpdateTs(ts); @@ -256,20 +272,14 @@ export class AggregatedValueCardWidgetComponent implements OnInit, AfterViewInit } public onResize() { - if (this.showChart) { - this.flot.resize(); - } } public onEditModeChanged() { - if (this.showChart) { - this.flot.checkMouseEvents(); - } } public onDestroy() { if (this.showChart) { - this.flot.destroy(); + this.lineChart.destroy(); } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.html index d945380a10..ba538d8104 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.html @@ -18,8 +18,10 @@
-
-
{{ valueText }}
+
+
+
{{ valueText }}
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.scss index f958bb6354..22e52e05dd 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.scss +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.scss @@ -52,7 +52,18 @@ } .tb-value-chart-card-chart { flex: 1; + min-width: 0; height: 100%; } + &.auto-scale { + .tb-value-chart-card-value-container { + width: 35%; + display: flex; + justify-content: flex-start; + } + .tb-value-chart-card-chart { + width: 65%; + } + } } } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.ts index d4b8404275..a7981b3acf 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/cards/value-chart-card-widget.component.ts @@ -34,7 +34,9 @@ import { ColorProcessor, ComponentStyle, getDataKey, - overlayStyle, resolveCssSize, + overlayStyle, + resolveCssSize, + simpleDateFormat, textStyle } from '@shared/models/widget-settings.models'; import { WidgetComponent } from '@home/components/widget/widget.component'; @@ -44,16 +46,21 @@ import { ValueChartCardLayout, ValueChartCardWidgetSettings } from '@home/components/widget/lib/cards/value-chart-card-widget.models'; -import { TbFlot } from '@home/components/widget/lib/flot-widget'; import { DataKey } from '@shared/models/widget.models'; -import { TbFlotKeySettings, TbFlotSettings } from '@home/components/widget/lib/flot-widget.models'; import { getTsValueByLatestDataKey } from '@home/components/widget/lib/cards/aggregated-value-card.models'; import { Observable } from 'rxjs'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; +import { TbTimeSeriesChart } from '@home/components/widget/lib/chart/time-series-chart'; +import { + TimeSeriesChartKeySettings, + TimeSeriesChartSeriesType, + TimeSeriesChartSettings +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { DeepPartial } from '@shared/models/common'; const layoutHeight = 56; -const valueRelativeMaxWidth = 0.5; +const valueRelativeWidth = 0.35; @Component({ selector: 'tb-value-chart-card-widget', @@ -81,6 +88,7 @@ export class ValueChartCardWidgetComponent implements OnInit, AfterViewInit, OnD widgetTitlePanel: TemplateRef; layout: ValueChartCardLayout; + autoScale: boolean; showValue = true; valueText = 'N/A'; @@ -90,8 +98,8 @@ export class ValueChartCardWidgetComponent implements OnInit, AfterViewInit, OnD backgroundStyle$: Observable; overlayStyle: ComponentStyle = {}; - private flot: TbFlot; - private flotDataKey: DataKey; + private lineChart: TbTimeSeriesChart; + private lineChartDataKey: DataKey; private valueKey: DataKey; private contentResize$: ResizeObserver; @@ -129,6 +137,7 @@ export class ValueChartCardWidgetComponent implements OnInit, AfterViewInit, OnD } this.layout = this.settings.layout; + this.autoScale = this.settings.autoScale; this.showValue = this.settings.showValue; this.valueStyle = textStyle(this.settings.valueFont); @@ -138,37 +147,37 @@ export class ValueChartCardWidgetComponent implements OnInit, AfterViewInit, OnD this.overlayStyle = overlayStyle(this.settings.background.overlay); if (this.ctx.defaultSubscription.firstDatasource?.dataKeys?.length) { - this.flotDataKey = this.ctx.defaultSubscription.firstDatasource?.dataKeys[0]; - this.flotDataKey.settings = { - fillLines: false, - showLines: true, - lineWidth: 2 - } as TbFlotKeySettings; + this.lineChartDataKey = this.ctx.defaultSubscription.firstDatasource?.dataKeys[0]; + this.lineChartDataKey.settings = { + type: TimeSeriesChartSeriesType.line, + lineSettings: { + showLine: true, + step: false, + smooth: true, + lineWidth: 2, + showPoints: false, + showPointLabel: false + } + } as TimeSeriesChartKeySettings; } } public ngAfterViewInit() { - const settings = { - shadowSize: 0, - enableSelection: false, - smoothLines: true, - grid: { - tickColor: 'rgba(0,0,0,0.12)', - horizontalLines: false, - verticalLines: false, - outlineWidth: 0, - minBorderMargin: 0, - margin: 0 + const settings: DeepPartial = { + dataZoom: false, + xAxis: { + show: false }, - yaxis: { - showLabels: false, - tickGenerator: 'return [(axis.max + axis.min) / 2];' + yAxes: { + default: { + show: false, + } }, - xaxis: { - showLabels: false - } - } as TbFlotSettings; - this.flot = new TbFlot(this.ctx, 'line', $(this.chartElement.nativeElement), settings); + tooltipDateInterval: false, + tooltipDateFormat: simpleDateFormat('dd MMM yyyy HH:mm:ss') + }; + + this.lineChart = new TbTimeSeriesChart(this.ctx, settings, this.chartElement.nativeElement, this.renderer, false); this.contentResize$ = new ResizeObserver(() => { this.onResize(); @@ -190,7 +199,9 @@ export class ValueChartCardWidgetComponent implements OnInit, AfterViewInit, OnD } public onDataUpdated() { - this.flot.update(); + if (this.lineChart) { + this.lineChart.update(); + } } public onLatestDataUpdated() { @@ -206,20 +217,21 @@ export class ValueChartCardWidgetComponent implements OnInit, AfterViewInit, OnD this.valueColor.update(value); this.cd.detectChanges(); setTimeout(() => { - this.onResize(false); + this.onResize(); }, 0); } } public onEditModeChanged() { - this.flot.checkMouseEvents(); } public onDestroy() { - this.flot.destroy(); + if (this.lineChart) { + this.lineChart.destroy(); + } } - private onResize(fitTargetHeight = true) { + private onResize(fitTargetWidth = true) { if (this.settings.autoScale && this.showValue) { const contentWidth = this.valueChartCardContent.nativeElement.getBoundingClientRect().width; const contentHeight = this.valueChartCardContent.nativeElement.getBoundingClientRect().height; @@ -228,26 +240,26 @@ export class ValueChartCardWidgetComponent implements OnInit, AfterViewInit, OnD this.valueFontSize = resolveCssSize(fontSize)[0]; } const valueRelativeHeight = Math.min(this.valueFontSize / layoutHeight, 1); - const targetValueHeight = contentHeight * valueRelativeHeight; - const maxValueWidth = contentWidth * valueRelativeMaxWidth; - this.setValueFontSize(targetValueHeight, maxValueWidth, fitTargetHeight); + const targetValueWidth = contentWidth * valueRelativeWidth; + const maxValueHeight = contentHeight * valueRelativeHeight; + this.setValueFontSize(targetValueWidth, maxValueHeight, fitTargetWidth); } - this.flot.resize(); + this.lineChart.resize(); } - private setValueFontSize(targetHeight: number, maxWidth: number, fitTargetHeight = true) { + private setValueFontSize(targetWidth: number, maxHeight: number, fitTargetWidth = true) { const fontSize = getComputedStyle(this.valueChartCardValue.nativeElement).fontSize; let valueFontSize = resolveCssSize(fontSize)[0]; this.renderer.setStyle(this.valueChartCardValue.nativeElement, 'fontSize', valueFontSize + 'px'); this.renderer.setStyle(this.valueChartCardValue.nativeElement, 'lineHeight', '1'); - let valueHeight = this.valueChartCardValue.nativeElement.getBoundingClientRect().height; - while (fitTargetHeight && valueHeight < targetHeight) { + let valueWidth = this.valueChartCardValue.nativeElement.getBoundingClientRect().width; + while (fitTargetWidth && valueWidth < targetWidth) { valueFontSize++; this.renderer.setStyle(this.valueChartCardValue.nativeElement, 'fontSize', valueFontSize + 'px'); - valueHeight = this.valueChartCardValue.nativeElement.getBoundingClientRect().height; + valueWidth = this.valueChartCardValue.nativeElement.getBoundingClientRect().width; } - let valueWidth = this.valueChartCardValue.nativeElement.getBoundingClientRect().width; - while ((valueHeight > targetHeight || valueWidth > maxWidth) && valueFontSize > 6) { + let valueHeight = this.valueChartCardValue.nativeElement.getBoundingClientRect().height; + while ((valueWidth > targetWidth || valueHeight > maxHeight) && valueFontSize > 6) { valueFontSize--; this.renderer.setStyle(this.valueChartCardValue.nativeElement, 'fontSize', valueFontSize + 'px'); valueWidth = this.valueChartCardValue.nativeElement.getBoundingClientRect().width; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts index 2e2faf35e8..4784a65c88 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.component.ts @@ -37,7 +37,6 @@ import { } from '@shared/models/widget-settings.models'; import { ResizeObserver } from '@juggle/resize-observer'; import { formatValue } from '@core/utils'; -import { DataKey } from '@shared/models/widget.models'; import { Observable } from 'rxjs'; import { ImagePipe } from '@shared/pipe/image.pipe'; import { DomSanitizer } from '@angular/platform-browser'; @@ -54,18 +53,13 @@ import { ECharts, echartsModule, EChartsOption, + EChartsSeriesItem, echartsTooltipFormatter, - NamedDataSet, toNamedData } from '@home/components/widget/lib/chart/echarts-widget.models'; import { IntervalMath } from '@shared/models/time/time.models'; -interface BarChartDataItem { - id: string; - dataKey: DataKey; - data: NamedDataSet; - enabled: boolean; -} +type BarChartDataItem = EChartsSeriesItem; interface BarChartLegendItem { id: string; @@ -386,7 +380,7 @@ export class BarChartWithLabelsWidgetComponent implements OnInit, OnDestroy, Aft tooltip: { trigger: 'axis', confine: true, - appendToBody: true, + appendTo: 'body', axisPointer: { type: 'shadow' }, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts index d0b54fed53..99ea088c92 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/bar-chart-with-labels-widget.models.ts @@ -75,6 +75,7 @@ export const barChartWithLabelsDefaultSettings: BarChartWithLabelsWidgetSettings }, tooltipValueColor: 'rgba(0, 0, 0, 0.76)', tooltipShowDate: true, + tooltipDateInterval: true, tooltipDateFormat: customDateFormat('MMMM y'), tooltipDateFont: { family: 'Roboto', diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.ts index 723b581fc5..d215f37a32 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/doughnut-widget.component.ts @@ -387,7 +387,7 @@ export class DoughnutWidgetComponent implements OnInit, OnDestroy, AfterViewInit tooltip: { trigger: this.settings.showTooltip ? 'item' : 'none', confine: false, - appendToBody: true + appendTo: 'body', }, series: [ { diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts index 515e47559d..c446fcbda1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/echarts-widget.models.ts @@ -17,35 +17,45 @@ import * as echarts from 'echarts/core'; import { Axis } from 'echarts'; import AxisModel from 'echarts/types/src/coord/cartesian/AxisModel'; -import { formatValue, isDefinedAndNotNull, isNumber, isString } from '@core/utils'; +import { estimateLabelUnionRect } from 'echarts/lib/coord/axisHelper'; +import { formatValue, isDefinedAndNotNull } from '@core/utils'; import TimeScale from 'echarts/types/src/scale/Time'; import { - DataZoomComponent, DataZoomComponentOption, - GridComponent, GridComponentOption, - MarkLineComponent, MarkLineComponentOption, - TooltipComponent, TooltipComponentOption, - VisualMapComponent, VisualMapComponentOption + DataZoomComponent, + DataZoomComponentOption, + GridComponent, + GridComponentOption, + MarkLineComponent, + MarkLineComponentOption, + TooltipComponent, + TooltipComponentOption, + VisualMapComponent, + VisualMapComponentOption } from 'echarts/components'; import { BarChart, - LineChart, + BarSeriesOption, CustomChart, CustomSeriesOption, + LineChart, LineSeriesOption, - BarSeriesOption, PieSeriesOption, PieChart + PieChart, + PieSeriesOption } from 'echarts/charts'; import { LabelLayout } from 'echarts/features'; import { CanvasRenderer, SVGRenderer } from 'echarts/renderers'; -import { DataEntry, DataSet } from '@shared/models/widget.models'; +import { DataEntry, DataKey, DataSet, LegendDirection } from '@shared/models/widget.models'; import { calculateAggIntervalWithWidgetTimeWindow, - Interval, IntervalMath, WidgetTimewindow } from '@shared/models/time/time.models'; import { CallbackDataParams } from 'echarts/types/dist/shared'; import { Renderer2 } from '@angular/core'; import { DateFormatProcessor, DateFormatSettings, Font } from '@shared/models/widget-settings.models'; +import GlobalModel from 'echarts/types/src/model/Global'; +import Axis2D from 'echarts/types/src/coord/cartesian/Axis2D'; +import SeriesModel from 'echarts/types/src/model/Series'; class EChartsModule { private initialized = false; @@ -139,6 +149,134 @@ export type EChartsDataItem = [number, any, number, number]; export type NamedDataSet = {name: string; value: EChartsDataItem}[]; +export type EChartsSeriesItem = { + id: string; + dataKey: DataKey; + data: NamedDataSet; + dataSet?: DataSet; + enabled: boolean; + units?: string; + decimals?: number; +}; + +export const getXAxis = (chart: ECharts): Axis2D => { + const model: GlobalModel = (chart as any).getModel(); + const models = model.queryComponents({mainType: 'xAxis'}); + if (models?.length) { + const axisModel = models[0] as AxisModel; + return axisModel.axis; + } + return null; +}; + +export const getYAxis = (chart: ECharts, axisId: string): Axis2D => { + const model: GlobalModel = (chart as any).getModel(); + const models = model.queryComponents({mainType: 'yAxis', id: axisId}); + if (models?.length) { + const axisModel = models[0] as AxisModel; + return axisModel.axis; + } + return null; +}; + +export const calculateYAxisWidth = (chart: ECharts, axisId: string): number => { + const axis = getYAxis(chart, axisId); + return calculateAxisSize(axis); +}; + +export const calculateXAxisHeight = (chart: ECharts): number => { + const axis = getXAxis(chart); + return calculateAxisSize(axis); +}; + +const calculateAxisSize = (axis: Axis2D): number => { + let size = 0; + if (axis && axis.model.option.show) { + const labelUnionRect = estimateLabelUnionRect(axis); + if (labelUnionRect) { + const margin = axis.model.get(['axisLabel', 'margin']); + const dimension = axis.isHorizontal() ? 'height' : 'width'; + size += labelUnionRect[dimension] + margin; + } + if (!axis.scale.isBlank() && axis.model.get(['axisTick', 'show'])) { + const tickLength = axis.model.get(['axisTick', 'length']); + size += tickLength; + } + } + return size; +}; + +export const measureYAxisNameWidth = (chart: ECharts, axisId: string, name: string): number => { + const axis = getYAxis(chart, axisId); + if (axis) { + return axis.model.getModel('nameTextStyle').getTextRect(name).height; + } + return 0; +}; + +export const measureXAxisNameHeight = (chart: ECharts, name: string): number => { + const axis = getXAxis(chart); + if (axis) { + return axis.model.getModel('nameTextStyle').getTextRect(name).height; + } + return 0; +}; + +export const measureThresholdLabelOffset = (chart: ECharts, axisId: string, thresholdId: string, value: any): [number, number] => { + const axis = getYAxis(chart, axisId); + if (axis && !axis.scale.isBlank()) { + const extent = axis.scale.getExtent(); + const model: GlobalModel = (chart as any).getModel(); + const models = model.queryComponents({mainType: 'series', id: thresholdId}); + if (models?.length) { + const lineSeriesModel = models[0] as SeriesModel; + const markLineModel = lineSeriesModel.getModel('markLine'); + const labelPosition = markLineModel.get(['label', 'position']); + if (labelPosition === 'start' || labelPosition === 'end') { + const labelModel = markLineModel.getModel('label'); + const formatter = markLineModel.get(['label', 'formatter']); + let textWidth = 0; + if (Array.isArray(value)) { + for (const val of value) { + if (val >= extent[0] && val <= extent[1]) { + const textVal = typeof formatter === 'string' ? formatter : formatter({value: val} as CallbackDataParams); + textWidth = Math.max(textWidth, labelModel.getTextRect(textVal).width); + } + } + } else { + if (value >= extent[0] && value <= extent[1]) { + const textVal = typeof formatter === 'string' ? formatter : formatter({value} as CallbackDataParams); + textWidth = labelModel.getTextRect(textVal).width; + } + } + if (!textWidth) { + return [0,0]; + } + const distanceOpt = markLineModel.get(['label', 'distance']); + let distance = 5; + if (distanceOpt) { + distance = typeof distanceOpt === 'number' ? distanceOpt : distanceOpt[0]; + } + const offset = distance + textWidth; + if (labelPosition === 'start') { + return [offset, 0]; + } else { + return [0, offset]; + } + } + } + } + return [0,0]; +}; + +export const getAxisExtent = (chart: ECharts, axisId: string): [number, number] => { + const axis = getYAxis(chart, axisId); + if (axis) { + return axis.scale.getExtent(); + } + return [0,0]; +}; + export const toNamedData = (data: DataSet): NamedDataSet => { if (!data?.length) { return []; @@ -162,11 +300,25 @@ const toEChartsDataItem = (entry: DataEntry): EChartsDataItem => { return item; }; +export enum EChartsTooltipTrigger { + point = 'point', + axis = 'axis' +} + +export const tooltipTriggerTranslationMap = new Map( + [ + [ EChartsTooltipTrigger.point, 'tooltip.trigger-point' ], + [ EChartsTooltipTrigger.axis, 'tooltip.trigger-axis' ] + ] +); + export interface EChartsTooltipWidgetSettings { showTooltip: boolean; + tooltipTrigger?: EChartsTooltipTrigger; tooltipValueFont: Font; tooltipValueColor: string; tooltipShowDate: boolean; + tooltipDateInterval?: boolean; tooltipDateFormat: DateFormatSettings; tooltipDateFont: Font; tooltipDateColor: string; @@ -177,13 +329,15 @@ export interface EChartsTooltipWidgetSettings { export const echartsTooltipFormatter = (renderer: Renderer2, tooltipDateFormat: DateFormatProcessor, settings: EChartsTooltipWidgetSettings, - params: CallbackDataParams[], + params: CallbackDataParams[] | CallbackDataParams, decimals: number, units: string, - focusedSeriesIndex: number): null | HTMLElement => { - if (!params.length || !params[0]) { + focusedSeriesIndex: number, + series?: EChartsSeriesItem[]): null | HTMLElement => { + if (!params || Array.isArray(params) && !params[0]) { return null; } + const firstParam = Array.isArray(params) ? params[0] : params; const tooltipElement: HTMLElement = renderer.createElement('div'); renderer.setStyle(tooltipElement, 'display', 'flex'); renderer.setStyle(tooltipElement, 'flex-direction', 'column'); @@ -192,9 +346,9 @@ export const echartsTooltipFormatter = (renderer: Renderer2, if (settings.tooltipShowDate) { const dateElement: HTMLElement = renderer.createElement('div'); let dateText: string; - const startTs = params[0].value[2]; - const endTs = params[0].value[3]; - if (startTs && endTs && (endTs - 1) > startTs) { + const startTs = firstParam.value[2]; + const endTs = firstParam.value[3]; + if (settings.tooltipDateInterval && startTs && endTs && (endTs - 1) > startTs) { const startDateText = tooltipDateFormat.update(startTs); const endDateText = tooltipDateFormat.update(endTs - 1); if (startDateText === endDateText) { @@ -203,7 +357,7 @@ export const echartsTooltipFormatter = (renderer: Renderer2, dateText = startDateText + ' - ' + endDateText; } } else { - const ts = params[0].value[0]; + const ts = firstParam.value[0]; dateText = tooltipDateFormat.update(ts); } renderer.appendChild(dateElement, renderer.createText(dateText)); @@ -216,14 +370,16 @@ export const echartsTooltipFormatter = (renderer: Renderer2, renderer.appendChild(tooltipElement, dateElement); } let seriesParams: CallbackDataParams = null; - if (focusedSeriesIndex > -1) { + if (Array.isArray(params) && focusedSeriesIndex > -1) { seriesParams = params.find(param => param.seriesIndex === focusedSeriesIndex); + } else if (!Array.isArray(params)) { + seriesParams = params; } if (seriesParams) { - renderer.appendChild(tooltipElement, constructEchartsTooltipSeriesElement(renderer, settings, seriesParams, decimals, units)); - } else { + renderer.appendChild(tooltipElement, constructEchartsTooltipSeriesElement(renderer, settings, seriesParams, decimals, units, series)); + } else if (Array.isArray(params)) { for (seriesParams of params) { - renderer.appendChild(tooltipElement, constructEchartsTooltipSeriesElement(renderer, settings, seriesParams, decimals, units)); + renderer.appendChild(tooltipElement, constructEchartsTooltipSeriesElement(renderer, settings, seriesParams, decimals, units, series)); } } return tooltipElement; @@ -233,7 +389,8 @@ const constructEchartsTooltipSeriesElement = (renderer: Renderer2, settings: EChartsTooltipWidgetSettings, seriesParams: CallbackDataParams, decimals: number, - units: string): HTMLElement => { + units: string, + series?: EChartsSeriesItem[]): HTMLElement => { const labelValueElement: HTMLElement = renderer.createElement('div'); renderer.setStyle(labelValueElement, 'display', 'flex'); renderer.setStyle(labelValueElement, 'flex-direction', 'row'); @@ -262,7 +419,16 @@ const constructEchartsTooltipSeriesElement = (renderer: Renderer2, renderer.setStyle(labelTextElement, 'color', 'rgba(0, 0, 0, 0.76)'); renderer.appendChild(labelElement, labelTextElement); const valueElement: HTMLElement = renderer.createElement('div'); - const value = formatValue(seriesParams.value[1], decimals, units, false); + let formatDecimals = decimals; + let formatUnits = units; + if (series) { + const item = series.find(s => s.id === seriesParams.seriesId); + if (item) { + formatDecimals = item.decimals; + formatUnits = item.units; + } + } + const value = formatValue(seriesParams.value[1], formatDecimals, formatUnits, false); renderer.appendChild(valueElement, renderer.createText(value)); renderer.setStyle(valueElement, 'flex', '1'); renderer.setStyle(valueElement, 'text-align', 'end'); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts index 0a8c15b247..9909be6532 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.component.ts @@ -318,7 +318,7 @@ export class RangeChartWidgetComponent implements OnInit, OnDestroy, AfterViewIn tooltip: { trigger: 'axis', confine: true, - appendToBody: true, + appendTo: 'body', axisPointer: { type: 'shadow' }, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts index c50813f7b6..31905e70ba 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/range-chart-widget.models.ts @@ -71,6 +71,7 @@ export const rangeChartDefaultSettings: RangeChartWidgetSettings = { }, tooltipValueColor: 'rgba(0, 0, 0, 0.76)', tooltipShowDate: true, + tooltipDateInterval: true, tooltipDateFormat: simpleDateFormat('dd MMM yyyy HH:mm'), tooltipDateFont: { family: 'Roboto', diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-bar.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-bar.models.ts new file mode 100644 index 0000000000..c00a078b2f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-bar.models.ts @@ -0,0 +1,166 @@ +/// +/// 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. +/// + +import { LinearGradientObject } from 'zrender/lib/graphic/LinearGradient'; +import { Interval, IntervalMath } from '@shared/models/time/time.models'; +import { LabelFormatterCallback, SeriesLabelOption } from 'echarts/types/src/util/types'; +import { + TimeSeriesChartDataItem, + TimeSeriesChartNoAggregationBarWidthStrategy +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { CustomSeriesRenderItemParams } from 'echarts'; +import { CustomSeriesRenderItemAPI, CustomSeriesRenderItemReturn } from 'echarts/types/dist/shared'; +import { isNumeric } from '@core/utils'; +import * as echarts from 'echarts/core'; + +export interface BarVisualSettings { + color: string | LinearGradientObject; + borderColor: string; + borderWidth: number; + borderRadius: number; +} + +export interface BarRenderSharedContext { + timeInterval: Interval; + noAggregationBarWidthStrategy: TimeSeriesChartNoAggregationBarWidthStrategy; + noAggregationWidthRelative: boolean; + noAggregationWidth: number; +} + +export interface BarRenderContext { + shared: BarRenderSharedContext; + barsCount?: number; + barIndex?: number; + noAggregation?: boolean; + visualSettings?: BarVisualSettings; + labelOption?: SeriesLabelOption; + barStackIndex?: number; + currentStackItems?: TimeSeriesChartDataItem[]; +} + +export const renderTimeSeriesBar = (params: CustomSeriesRenderItemParams, api: CustomSeriesRenderItemAPI, + renderCtx: BarRenderContext): CustomSeriesRenderItemReturn => { + const time = api.value(0) as number; + let start = api.value(2) as number; + let end = api.value(3) as number; + let interval = end - start; + const ts = start ? start : time; + + const separateBar = renderCtx.noAggregation && + renderCtx.shared.noAggregationBarWidthStrategy === TimeSeriesChartNoAggregationBarWidthStrategy.separate; + + if (renderCtx.noAggregation) { + if (renderCtx.shared.noAggregationWidthRelative) { + const scaleWidth = api.getWidth() / api.size([1,0])[0]; + interval = scaleWidth * (renderCtx.shared.noAggregationWidth / 100); + } else { + interval = renderCtx.shared.noAggregationWidth; + } + start = time - interval / 2; + end = time + interval / 2; + } + if (!start || !end || !interval) { + interval = IntervalMath.numberValue(renderCtx.shared.timeInterval); + start = time - interval / 2; + } + + const gap = 0.3; + const barInterval = separateBar ? interval : interval / (renderCtx.barsCount + gap * (renderCtx.barsCount + 3)); + const intervalGap = barInterval * gap * 2; + const barGap = barInterval * gap; + const value = api.value(1); + const startTime = separateBar ? start : start + intervalGap + (barInterval + barGap) * renderCtx.barIndex; + const delta = barInterval; + let offset = 0; + if (renderCtx.currentStackItems?.length) { + for (let i = 0; i < renderCtx.barStackIndex; i++) { + const stackItem = renderCtx.currentStackItems[i]; + const dataName = ts + ''; + const data = stackItem.data.find(d => d.name === dataName); + if (data) { + const val = data.value[1]; + if (isNumeric(val)) { + offset += Number(val); + } + } + } + } + let lowerLeft: number[]; + if (offset !== 0 && isNumeric(value)) { + lowerLeft = api.coord([startTime, value >= 0 ? Number(value) + offset : offset]); + } else { + lowerLeft = api.coord([startTime, value >= 0 ? value : 0]); + } + const size = api.size([delta, value]); + const width = size[0]; + const height = size[1]; + + const coordSys: {x: number; y: number; width: number; height: number} = params.coordSys as any; + + const rectShape = echarts.graphic.clipRectByRect({ + x: lowerLeft[0], + y: lowerLeft[1], + width, + height + }, { + x: coordSys.x, + y: coordSys.y, + width: coordSys.width, + height: coordSys.height + }); + + const zeroPos = api.coord([0, offset]); + + const style: any = { + fill: renderCtx.visualSettings.color, + stroke: renderCtx.visualSettings.borderColor, + lineWidth: renderCtx.visualSettings.borderWidth + }; + + if (renderCtx.labelOption.show) { + let position = renderCtx.labelOption.position; + if (value < 0) { + if (position === 'top') { + position = 'bottom'; + } else if (position === 'bottom') { + position = 'top'; + } + } + style.text = (renderCtx.labelOption.formatter as LabelFormatterCallback)({value: [null, value]} as any); + style.textDistance = 5; + style.textPosition = position; + style.rich = renderCtx.labelOption.rich; + } + + let borderRadius: number[]; + if (value < 0) { + borderRadius = [0, 0, renderCtx.visualSettings.borderRadius, renderCtx.visualSettings.borderRadius]; + } else { + borderRadius = [renderCtx.visualSettings.borderRadius, renderCtx.visualSettings.borderRadius, 0, 0]; + } + return rectShape && { + type: 'rect', + id: time + '', + shape: {...rectShape, r: borderRadius}, + style, + focus: 'series', + transition: 'all', + enterFrom: { + style: { opacity: 0 }, + shape: { height: 0, y: zeroPos[1] } + } + }; +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.html new file mode 100644 index 0000000000..4430579899 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.html @@ -0,0 +1,123 @@ + +
+
+ +
+
+
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + +
{{ 'legend.Min' | translate }}{{ 'legend.Max' | translate }}{{ 'legend.Avg' | translate }}{{ 'legend.Total' | translate }}{{ 'legend.Latest' | translate }}
+ + + {{ legendData.data[legendKey.dataIndex].min }} + + {{ legendData.data[legendKey.dataIndex].max }} + + {{ legendData.data[legendKey.dataIndex].avg }} + + {{ legendData.data[legendKey.dataIndex].total }} + + {{ legendData.data[legendKey.dataIndex].latest }} +
+
+ + + + + + +
+
+
+
{{ legendKey.dataKey.label }}
+
+
+
+ + + {{ label | translate }} + + {{ legendData.data[legendKey.dataIndex][type] }} + + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.scss new file mode 100644 index 0000000000..ee0400d96f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.scss @@ -0,0 +1,172 @@ +/** + * 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. + */ + +$maxLegendWidth: 25%; +$maxLegendHeight: 35%; + +.tb-time-series-chart-panel { + width: 100%; + height: 100%; + position: relative; + display: flex; + flex-direction: column; + gap: 8px; + &.overlay { + padding: 20px 24px 24px 24px; + } + > div:not(.tb-time-series-chart-overlay) { + z-index: 1; + } + .tb-time-series-chart-overlay { + position: absolute; + top: 12px; + left: 12px; + bottom: 12px; + right: 12px; + } + div.tb-widget-title { + padding: 0; + } + .tb-time-series-chart-content { + flex: 1; + min-width: 0; + min-height: 0; + display: flex; + flex-direction: column; + gap: 8px; + &.legend-top { + flex-direction: column-reverse; + } + &.legend-right { + flex-direction: row; + } + &.legend-left { + flex-direction: row-reverse; + } + .tb-time-series-chart-shape { + flex: 1; + min-width: 0; + min-height: 0; + display: flex; + align-items: center; + justify-content: center; + } + &.legend-right, &.legend-left { + .tb-time-series-chart-legend { + display: inline-grid; + grid-auto-flow: column; + grid-template-rows: repeat(auto-fit, minmax(16px, min-content)); + max-width: calc($maxLegendWidth - 8px); + height: fit-content; + max-height: 100%; + } + } + &.legend-top, &.legend-bottom { + .tb-time-series-chart-legend { + align-self: center; + &.tb-simple-legend { + justify-content: center; + } + &:not(.tb-simple-legend) { + width: 100%; + } + } + } + .tb-time-series-chart-legend { + display: flex; + align-items: flex-start; + align-self: stretch; + column-gap: 16px; + row-gap: 8px; + flex-wrap: wrap; + overflow: auto; + width: fit-content; + max-width: 100%; + max-height: calc($maxLegendHeight - 8px); + .tb-time-series-chart-legend-table { + border-spacing: 0; + table-layout: fixed; + &.vertical { + width: 100%; + table-layout: auto; + tbody { + th { + width: 95%; + } + } + } + th, td { + &:not(:last-child) { + padding-right: 16px; + } + } + thead tr, tbody tr:not(:last-child) { + th, td { + padding-bottom: 8px; + } + } + .tb-time-series-chart-legend-item { + align-items: flex-end; + &.left { + align-items: flex-start; + } + } + } + .tb-time-series-chart-legend-item { + display: flex; + flex-direction: column; + justify-content: center; + align-items: flex-start; + user-select: none; + .tb-time-series-chart-legend-item-label { + display: flex; + align-items: center; + gap: 4px; + color: #ccc; + white-space: nowrap; + cursor: pointer; + .tb-time-series-chart-legend-item-label-circle { + width: 8px; + height: 8px; + border-radius: 50%; + background-color: #ccc; + } + } + } + .tb-time-series-chart-legend-type-label { + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 16px; + color: rgba(0, 0, 0, 0.38); + white-space: nowrap; + text-align: left; + &.right { + text-align: right; + } + } + .tb-time-series-chart-legend-value { + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: 16px; + color: rgba(0, 0, 0, 0.87); + white-space: nowrap; + text-align: right; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts new file mode 100644 index 0000000000..fbb23c6c84 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.component.ts @@ -0,0 +1,167 @@ +/// +/// 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. +/// + +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + Input, + OnDestroy, + OnInit, + Renderer2, + TemplateRef, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { timeSeriesChartKeyDefaultSettings, TimeSeriesChartKeySettings } from '@home/components/widget/lib/chart/time-series-chart.models'; +import { WidgetContext } from '@home/models/widget-component.models'; +import { Observable } from 'rxjs'; +import { backgroundStyle, ComponentStyle, overlayStyle, textStyle } from '@shared/models/widget-settings.models'; +import { ImagePipe } from '@shared/pipe/image.pipe'; +import { DomSanitizer } from '@angular/platform-browser'; +import { LegendConfig, LegendData, LegendKey, LegendPosition } from '@shared/models/widget.models'; +import { TbTimeSeriesChart } from '@home/components/widget/lib/chart/time-series-chart'; +import { + timeSeriesChartWidgetDefaultSettings, + TimeSeriesChartWidgetSettings +} from '@home/components/widget/lib/chart/time-series-chart-widget.models'; +import { mergeDeep } from '@core/utils'; + +@Component({ + selector: 'tb-time-series-chart-widget', + templateUrl: './time-series-chart-widget.component.html', + styleUrls: ['./time-series-chart-widget.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class TimeSeriesChartWidgetComponent implements OnInit, OnDestroy, AfterViewInit { + + @ViewChild('chartShape', {static: false}) + chartShape: ElementRef; + + settings: TimeSeriesChartWidgetSettings; + + @Input() + ctx: WidgetContext; + + @Input() + widgetTitlePanel: TemplateRef; + + horizontalLegendPosition = false; + + showLegend: boolean; + legendClass: string; + legendConfig: LegendConfig; + legendData: LegendData; + legendKeys: LegendKey[]; + + backgroundStyle$: Observable; + overlayStyle: ComponentStyle = {}; + overlayEnabled: boolean; + padding: string; + + legendLabelStyle: ComponentStyle; + disabledLegendLabelStyle: ComponentStyle; + + displayLegendValues = false; + + private timeSeriesChart: TbTimeSeriesChart; + + constructor(private imagePipe: ImagePipe, + private sanitizer: DomSanitizer, + private renderer: Renderer2, + private cd: ChangeDetectorRef) { + } + + ngOnInit(): void { + this.ctx.$scope.timeSeriesChartWidget = this; + this.settings = {...timeSeriesChartWidgetDefaultSettings, ...this.ctx.settings}; + + this.backgroundStyle$ = backgroundStyle(this.settings.background, this.imagePipe, this.sanitizer); + this.overlayStyle = overlayStyle(this.settings.background.overlay); + this.overlayEnabled = this.settings.background.overlay.enabled; + this.padding = this.overlayEnabled ? undefined : this.settings.padding; + + this.showLegend = this.settings.showLegend; + if (this.showLegend) { + this.legendData = this.ctx.defaultSubscription.legendData; + this.legendConfig = this.settings.legendConfig; + this.legendKeys = this.legendData.keys; + if (this.legendConfig.sortDataKeys) { + this.legendKeys = this.legendData.keys.sort((key1, key2) => key1.dataKey.label.localeCompare(key2.dataKey.label)); + } + this.legendKeys.forEach(legendKey => { + legendKey.dataKey.settings = mergeDeep({} as TimeSeriesChartKeySettings, + timeSeriesChartKeyDefaultSettings, legendKey.dataKey.settings); + legendKey.dataKey.hidden = legendKey.dataKey.settings.dataHiddenByDefault; + }); + this.legendKeys = this.legendKeys.filter(legendKey => legendKey.dataKey.settings.showInLegend); + if (!this.legendKeys.length) { + this.showLegend = false; + } + } + + if (this.showLegend) { + this.horizontalLegendPosition = [LegendPosition.left, LegendPosition.right].includes(this.legendConfig.position); + this.legendClass = `legend-${this.legendConfig.position}`; + this.legendLabelStyle = textStyle(this.settings.legendLabelFont); + this.disabledLegendLabelStyle = textStyle(this.settings.legendLabelFont); + this.legendLabelStyle.color = this.settings.legendLabelColor; + this.displayLegendValues = this.legendConfig.showMin || this.legendConfig.showMax || + this.legendConfig.showAvg || this.legendConfig.showTotal || this.legendConfig.showLatest; + } + } + + ngAfterViewInit() { + this.timeSeriesChart = new TbTimeSeriesChart(this.ctx, this.settings, this.chartShape.nativeElement, this.renderer); + } + + ngOnDestroy() { + if (this.timeSeriesChart) { + this.timeSeriesChart.destroy(); + } + } + + public onInit() { + const borderRadius = this.ctx.$widgetElement.css('borderRadius'); + this.overlayStyle = {...this.overlayStyle, ...{borderRadius}}; + this.cd.detectChanges(); + } + + public onDataUpdated() { + if (this.timeSeriesChart) { + this.timeSeriesChart.update(); + } + } + + public onLatestDataUpdated() { + if (this.timeSeriesChart) { + this.timeSeriesChart.latestUpdated(); + } + } + + public onLegendKeyEnter(legendKey: LegendKey) { + this.timeSeriesChart.keyEnter(legendKey.dataKey); + } + + public onLegendKeyLeave(legendKey: LegendKey) { + this.timeSeriesChart.keyLeave(legendKey.dataKey); + } + + public toggleLegendKey(legendKey: LegendKey) { + this.timeSeriesChart.toggleKey(legendKey.dataKey); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.models.ts new file mode 100644 index 0000000000..6edf87251c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart-widget.models.ts @@ -0,0 +1,54 @@ +/// +/// 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. +/// + +import { timeSeriesChartDefaultSettings, TimeSeriesChartSettings } from '@home/components/widget/lib/chart/time-series-chart.models'; +import { BackgroundSettings, BackgroundType, Font } from '@shared/models/widget-settings.models'; +import { defaultLegendConfig, LegendConfig, LegendPosition, widgetType } from '@shared/models/widget.models'; +import { mergeDeep } from '@core/utils'; + +export interface TimeSeriesChartWidgetSettings extends TimeSeriesChartSettings { + showLegend: boolean; + legendLabelFont: Font; + legendLabelColor: string; + legendConfig: LegendConfig; + background: BackgroundSettings; + padding: string; +} + +export const timeSeriesChartWidgetDefaultSettings: TimeSeriesChartWidgetSettings = + mergeDeep({} as TimeSeriesChartWidgetSettings, timeSeriesChartDefaultSettings as TimeSeriesChartWidgetSettings, { + showLegend: true, + legendLabelFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '16px' + }, + legendLabelColor: 'rgba(0, 0, 0, 0.76)', + legendConfig: {...defaultLegendConfig(widgetType.timeseries), position: LegendPosition.top}, + background: { + type: BackgroundType.color, + color: '#fff', + overlay: { + enabled: false, + color: 'rgba(255,255,255,0.72)', + blur: 3 + } + }, + padding: '12px' + } as TimeSeriesChartWidgetSettings); diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts new file mode 100644 index 0000000000..b5d5968565 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.models.ts @@ -0,0 +1,1269 @@ +/// +/// 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. +/// + +import { + ECharts, + EChartsOption, + EChartsSeriesItem, + EChartsTooltipTrigger, + EChartsTooltipWidgetSettings, + measureThresholdLabelOffset +} from '@home/components/widget/lib/chart/echarts-widget.models'; +import { ComponentStyle, Font, simpleDateFormat, textStyle } from '@shared/models/widget-settings.models'; +import { XAXisOption, YAXisOption } from 'echarts/types/dist/shared'; +import { CustomSeriesOption, LineSeriesOption } from 'echarts/charts'; +import { + formatValue, + isDefinedAndNotNull, + isNumeric, + isUndefined, + isUndefinedOrNull, + mergeDeep, + parseFunction +} from '@core/utils'; +import { LinearGradientObject } from 'zrender/lib/graphic/LinearGradient'; +import tinycolor from 'tinycolor2'; +import Axis2D from 'echarts/types/src/coord/cartesian/Axis2D'; +import { ValueAxisBaseOption } from 'echarts/types/src/coord/axisCommonTypes'; +import { SeriesLabelOption } from 'echarts/types/src/util/types'; +import { + BarRenderContext, + BarRenderSharedContext, + BarVisualSettings, + renderTimeSeriesBar +} from '@home/components/widget/lib/chart/time-series-chart-bar.models'; +import { DataKey } from '@shared/models/widget.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { TbColorScheme } from '@shared/models/color.models'; +import { AbstractControl, ValidationErrors } from '@angular/forms'; +import { MarkLine2DDataItemOption } from 'echarts/types/src/component/marker/MarkLineModel'; + +export enum TimeSeriesChartType { + default = 'default', + line = 'line', + bar = 'bar', + point = 'point' +} + +export const timeSeriesChartTypeTranslations = new Map( + [ + [TimeSeriesChartType.line, 'widgets.time-series-chart.type-line'], + [TimeSeriesChartType.bar, 'widgets.time-series-chart.type-bar'], + [TimeSeriesChartType.point, 'widgets.time-series-chart.type-point'] + ] +); + +const timeSeriesChartColorScheme: TbColorScheme = { + 'threshold.line': { + light: 'rgba(0, 0, 0, 0.76)', + dark: '#eee' + }, + 'threshold.label': { + light: 'rgba(0, 0, 0, 0.76)', + dark: '#eee' + }, + 'axis.line': { + light: 'rgba(0, 0, 0, 0.54)', + dark: '#B9B8CE' + }, + 'axis.label': { + light: 'rgba(0, 0, 0, 0.54)', + dark: '#B9B8CE' + }, + 'axis.ticks': { + light: 'rgba(0, 0, 0, 0.54)', + dark: '#B9B8CE' + }, + 'axis.tickLabel': { + light: 'rgba(0, 0, 0, 0.54)', + dark: '#B9B8CE' + }, + 'axis.splitLine': { + light: 'rgba(0, 0, 0, 0.12)', + dark: '#484753' + }, + 'series.label': { + light: 'rgba(0, 0, 0, 0.76)', + dark: '#eee' + } +}; + +export enum AxisPosition { + left = 'left', + right = 'right', + top = 'top', + bottom = 'bottom' +} + +export const timeSeriesAxisPositions = Object.keys(AxisPosition) as AxisPosition[]; + +export const timeSeriesAxisPositionTranslations = new Map( + [ + [AxisPosition.left, 'widgets.time-series-chart.axis.position-left'], + [AxisPosition.right, 'widgets.time-series-chart.axis.position-right'], + [AxisPosition.top, 'widgets.time-series-chart.axis.position-top'], + [AxisPosition.bottom, 'widgets.time-series-chart.axis.position-bottom'] + ] +); + +export enum TimeSeriesChartShape { + emptyCircle = 'emptyCircle', + circle = 'circle', + rect = 'rect', + roundRect = 'roundRect', + triangle = 'triangle', + diamond = 'diamond', + pin = 'pin', + arrow = 'arrow', + none = 'none' +} + +export const timeSeriesChartShapes = Object.keys(TimeSeriesChartShape) as TimeSeriesChartShape[]; + +export const timeSeriesChartShapeTranslations = new Map( + [ + [TimeSeriesChartShape.emptyCircle, 'widgets.time-series-chart.shape-empty-circle'], + [TimeSeriesChartShape.circle, 'widgets.time-series-chart.shape-circle'], + [TimeSeriesChartShape.rect, 'widgets.time-series-chart.shape-rect'], + [TimeSeriesChartShape.roundRect, 'widgets.time-series-chart.shape-round-rect'], + [TimeSeriesChartShape.triangle, 'widgets.time-series-chart.shape-triangle'], + [TimeSeriesChartShape.diamond, 'widgets.time-series-chart.shape-diamond'], + [TimeSeriesChartShape.pin, 'widgets.time-series-chart.shape-pin'], + [TimeSeriesChartShape.arrow, 'widgets.time-series-chart.shape-arrow'], + [TimeSeriesChartShape.none, 'widgets.time-series-chart.shape-none'] + ] +); + +export enum TimeSeriesChartLineType { + solid = 'solid', + dashed = 'dashed', + dotted = 'dotted' +} + +export const timeSeriesLineTypes = Object.keys(TimeSeriesChartLineType) as TimeSeriesChartLineType[]; + +export const timeSeriesLineTypeTranslations = new Map( + [ + [TimeSeriesChartLineType.solid, 'widgets.time-series-chart.line-type-solid'], + [TimeSeriesChartLineType.dashed, 'widgets.time-series-chart.line-type-dashed'], + [TimeSeriesChartLineType.dotted, 'widgets.time-series-chart.line-type-dotted'] + ] +); + +export enum ThresholdLabelPosition { + start = 'start', + middle = 'middle', + end = 'end', + insideStart = 'insideStart', + insideStartTop = 'insideStartTop', + insideStartBottom = 'insideStartBottom', + insideMiddle = 'insideMiddle', + insideMiddleTop = 'insideMiddleTop', + insideMiddleBottom = 'insideMiddleBottom', + insideEnd = 'insideEnd', + insideEndTop = 'insideEndTop', + insideEndBottom = 'insideEndBottom' +} + +export const timeSeriesThresholdLabelPositions = Object.keys(ThresholdLabelPosition) as ThresholdLabelPosition[]; + +export const timeSeriesThresholdLabelPositionTranslations = new Map( + [ + [ThresholdLabelPosition.start, 'widgets.time-series-chart.threshold.label-position-start'], + [ThresholdLabelPosition.middle, 'widgets.time-series-chart.threshold.label-position-middle'], + [ThresholdLabelPosition.end, 'widgets.time-series-chart.threshold.label-position-end'], + [ThresholdLabelPosition.insideStart, 'widgets.time-series-chart.threshold.label-position-inside-start'], + [ThresholdLabelPosition.insideStartTop, 'widgets.time-series-chart.threshold.label-position-inside-start-top'], + [ThresholdLabelPosition.insideStartBottom, 'widgets.time-series-chart.threshold.label-position-inside-start-bottom'], + [ThresholdLabelPosition.insideMiddle, 'widgets.time-series-chart.threshold.label-position-inside-middle'], + [ThresholdLabelPosition.insideMiddleTop, 'widgets.time-series-chart.threshold.label-position-inside-middle-top'], + [ThresholdLabelPosition.insideMiddleBottom, 'widgets.time-series-chart.threshold.label-position-inside-middle-bottom'], + [ThresholdLabelPosition.insideEnd, 'widgets.time-series-chart.threshold.label-position-inside-end'], + [ThresholdLabelPosition.insideEndTop, 'widgets.time-series-chart.threshold.label-position-inside-end-top'], + [ThresholdLabelPosition.insideEndBottom, 'widgets.time-series-chart.threshold.label-position-inside-end-bottom'] + ] +); + +export enum TimeSeriesChartThresholdType { + constant = 'constant', + latestKey = 'latestKey', + entity = 'entity' +} + +export const timeSeriesThresholdTypes = Object.keys(TimeSeriesChartThresholdType) as TimeSeriesChartThresholdType[]; + +export const timeSeriesThresholdTypeTranslations = new Map( + [ + [TimeSeriesChartThresholdType.constant, 'widgets.time-series-chart.threshold.type-constant'], + [TimeSeriesChartThresholdType.latestKey, 'widgets.time-series-chart.threshold.type-latest-key'], + [TimeSeriesChartThresholdType.entity, 'widgets.time-series-chart.threshold.type-entity'] + ] +); + +export enum SeriesFillType { + none = 'none', + opacity = 'opacity', + gradient = 'gradient' +} + +export const seriesFillTypes = Object.keys(SeriesFillType) as SeriesFillType[]; + +export const seriesFillTypeTranslations = new Map( + [ + [SeriesFillType.none, 'widgets.time-series-chart.series.fill-type-none'], + [SeriesFillType.opacity, 'widgets.time-series-chart.series.fill-type-opacity'], + [SeriesFillType.gradient, 'widgets.time-series-chart.series.fill-type-gradient'] + ] +); + +export enum SeriesLabelPosition { + top = 'top', + bottom = 'bottom' +} + +export const seriesLabelPositions = Object.keys(SeriesLabelPosition) as SeriesLabelPosition[]; + +export const seriesLabelPositionTranslations = new Map( + [ + [SeriesLabelPosition.top, 'widgets.time-series-chart.series.label-position-top'], + [SeriesLabelPosition.bottom, 'widgets.time-series-chart.series.label-position-bottom'] + ] +); + +export enum LineSeriesStepType { + start = 'start', + middle = 'middle', + end = 'end' +} + +export const lineSeriesStepTypes = Object.keys(LineSeriesStepType) as LineSeriesStepType[]; + +export const lineSeriesStepTypeTranslations = new Map( + [ + [LineSeriesStepType.start, 'widgets.time-series-chart.series.line.step-type-start'], + [LineSeriesStepType.middle, 'widgets.time-series-chart.series.line.step-type-middle'], + [LineSeriesStepType.end, 'widgets.time-series-chart.series.line.step-type-end'] + ] +); + +export enum TimeSeriesChartSeriesType { + line = 'line', + bar = 'bar' +} + +export const timeSeriesChartSeriesTypes = Object.keys(TimeSeriesChartSeriesType) as TimeSeriesChartSeriesType[]; + +export const timeSeriesChartSeriesTypeTranslations = new Map( + [ + [TimeSeriesChartSeriesType.line, 'widgets.time-series-chart.series.type-line'], + [TimeSeriesChartSeriesType.bar, 'widgets.time-series-chart.series.type-bar'] + ] +); + +export const timeSeriesChartSeriesTypeIcons = new Map( + [ + [TimeSeriesChartSeriesType.line, 'mdi:chart-line'], + [TimeSeriesChartSeriesType.bar, 'mdi:chart-bar'] + ] +); + +export interface TimeSeriesChartAxisSettings { + show: boolean; + label?: string; + labelFont?: Font; + labelColor?: string; + position: AxisPosition; + showTickLabels: boolean; + tickLabelFont: Font; + tickLabelColor: string; + showTicks: boolean; + ticksColor: string; + showLine: boolean; + lineColor: string; + showSplitLines: boolean; + splitLinesColor: string; +} + +export type TimeSeriesChartYAxisId = 'default' | string; + +export interface TimeSeriesChartYAxisSettings extends TimeSeriesChartAxisSettings { + id?: TimeSeriesChartYAxisId; + order?: number; + units?: string; + decimals?: number; + min?: number | string; + max?: number | string; + intervalCalculator?: string; + ticksFormatter?: string; +} + +export const timeSeriesChartYAxisValid = (axis: TimeSeriesChartYAxisSettings): boolean => + !(!axis.id || isUndefinedOrNull(axis.order)); + +export const timeSeriesChartYAxisValidator = (control: AbstractControl): ValidationErrors | null => { + const axis: TimeSeriesChartYAxisSettings = control.value; + if (!timeSeriesChartYAxisValid(axis)) { + return { + axis: true + }; + } + return null; +}; + +export const getNextTimeSeriesYAxisId = (axes: TimeSeriesChartYAxisSettings[]): TimeSeriesChartYAxisId => { + let id = 0; + for (const axis of axes) { + const existingId = axis.id; + if (existingId.startsWith('axis')) { + const idSuffix = existingId.substring('axis'.length); + if (isNumeric(idSuffix)) { + id = Math.max(id, Number(idSuffix)); + } + } + } + return 'axis' + (id+1); +}; + +export const defaultTimeSeriesChartYAxisSettings: TimeSeriesChartYAxisSettings = { + units: null, + decimals: 0, + show: true, + label: '', + labelFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '600', + lineHeight: '1' + }, + labelColor: timeSeriesChartColorScheme['axis.label'].light, + position: AxisPosition.left, + showTickLabels: true, + tickLabelFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '1' + }, + tickLabelColor: timeSeriesChartColorScheme['axis.tickLabel'].light, + ticksFormatter: null, + showTicks: true, + ticksColor: timeSeriesChartColorScheme['axis.ticks'].light, + showLine: true, + lineColor: timeSeriesChartColorScheme['axis.line'].light, + showSplitLines: true, + splitLinesColor: timeSeriesChartColorScheme['axis.splitLine'].light +}; + +export type TimeSeriesChartYAxes = {[id: TimeSeriesChartYAxisId]: TimeSeriesChartYAxisSettings}; + +export interface TimeSeriesChartThreshold { + type: TimeSeriesChartThresholdType; + value?: number; + latestKey?: string; + latestKeyType?: DataKeyType.attribute | DataKeyType.timeseries; + entityAlias?: string; + entityKey?: string; + entityKeyType?: DataKeyType.attribute | DataKeyType.timeseries; + yAxisId: TimeSeriesChartYAxisId; + units?: string; + decimals?: number; + lineColor: string; + lineType: TimeSeriesChartLineType; + lineWidth: number; + startSymbol: TimeSeriesChartShape; + startSymbolSize: number; + endSymbol: TimeSeriesChartShape; + endSymbolSize: number; + showLabel: boolean; + labelPosition: ThresholdLabelPosition; + labelFont: Font; + labelColor: string; +} + +export const timeSeriesChartThresholdValid = (threshold: TimeSeriesChartThreshold): boolean => { + if (!threshold.type || !threshold.yAxisId) { + return false; + } + switch (threshold.type) { + case TimeSeriesChartThresholdType.constant: + if (isUndefinedOrNull(threshold.value)) { + return false; + } + break; + case TimeSeriesChartThresholdType.latestKey: + if (!threshold.latestKey || !threshold.latestKeyType) { + return false; + } + break; + case TimeSeriesChartThresholdType.entity: + if (!threshold.entityAlias || !threshold.entityKey || !threshold.entityKeyType) { + return false; + } + break; + } + return true; +}; + +export const timeSeriesChartThresholdValidator = (control: AbstractControl): ValidationErrors | null => { + const threshold: TimeSeriesChartThreshold = control.value; + if (!timeSeriesChartThresholdValid(threshold)) { + return { + threshold: true + }; + } + return null; +}; + +export const timeSeriesChartThresholdDefaultSettings: TimeSeriesChartThreshold = { + type: TimeSeriesChartThresholdType.constant, + yAxisId: 'default', + units: null, + decimals: 0, + lineColor: timeSeriesChartColorScheme['threshold.line'].light, + lineType: TimeSeriesChartLineType.solid, + lineWidth: 1, + startSymbol: TimeSeriesChartShape.none, + startSymbolSize: 5, + endSymbol: TimeSeriesChartShape.arrow, + endSymbolSize: 5, + showLabel: true, + labelPosition: ThresholdLabelPosition.end, + labelFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '1' + }, + labelColor: timeSeriesChartColorScheme['threshold.label'].light +}; + +export enum TimeSeriesChartNoAggregationBarWidthStrategy { + group = 'group', + separate = 'separate' +} + +export const timeSeriesChartNoAggregationBarWidthStrategies = + Object.keys(TimeSeriesChartNoAggregationBarWidthStrategy) as TimeSeriesChartNoAggregationBarWidthStrategy[]; + +export const timeSeriesChartNoAggregationBarWidthStrategyTranslations = new Map( + [ + [TimeSeriesChartNoAggregationBarWidthStrategy.group, 'widgets.time-series-chart.no-aggregation-bar-width-strategy-group'], + [TimeSeriesChartNoAggregationBarWidthStrategy.separate, 'widgets.time-series-chart.no-aggregation-bar-width-strategy-separate'] + ] +); + +export interface TimeSeriesChartBarWidth { + relative?: boolean; + relativeWidth?: number; + absoluteWidth?: number; +} + +export interface TimeSeriesChartNoAggregationBarWidthSettings { + strategy: TimeSeriesChartNoAggregationBarWidthStrategy; + groupWidth?: TimeSeriesChartBarWidth; + barWidth?: TimeSeriesChartBarWidth; +} + +export enum TimeSeriesChartAnimationEasing { + linear = 'linear', + quadraticIn = 'quadraticIn', + quadraticOut = 'quadraticOut', + quadraticInOut = 'quadraticInOut', + cubicIn = 'cubicIn', + cubicOut = 'cubicOut', + cubicInOut = 'cubicInOut', + quarticIn = 'quarticIn', + quarticOut = 'quarticOut', + quarticInOut = 'quarticInOut', + quinticIn = 'quinticIn', + quinticOut = 'quinticOut', + quinticInOut = 'quinticInOut', + sinusoidalIn = 'sinusoidalIn', + sinusoidalOut = 'sinusoidalOut', + sinusoidalInOut = 'sinusoidalInOut', + exponentialIn = 'exponentialIn', + exponentialOut = 'exponentialOut', + exponentialInOut = 'exponentialInOut', + circularIn = 'circularIn', + circularOut = 'circularOut', + circularInOut = 'circularInOut', + elasticIn = 'elasticIn', + elasticOut = 'elasticOut', + elasticInOut = 'elasticInOut', + backIn = 'backIn', + backOut = 'backOut', + backInOut = 'backInOut', + bounceIn = 'bounceIn', + bounceOut = 'bounceOut', + bounceInOut = 'bounceInOut' +} + +export const timeSeriesChartAnimationEasings = Object.keys(TimeSeriesChartAnimationEasing) as TimeSeriesChartAnimationEasing[]; + +export interface TimeSeriesChartAnimationSettings { + animation: boolean; + animationThreshold: number; + animationDuration: number; + animationEasing: TimeSeriesChartAnimationEasing; + animationDelay: number; + animationDurationUpdate: number; + animationEasingUpdate: TimeSeriesChartAnimationEasing; + animationDelayUpdate: number; +} + +export interface TimeSeriesChartSettings extends EChartsTooltipWidgetSettings { + thresholds: TimeSeriesChartThreshold[]; + darkMode: boolean; + dataZoom: boolean; + stack: boolean; + yAxes: TimeSeriesChartYAxes; + xAxis: TimeSeriesChartAxisSettings; + animation: TimeSeriesChartAnimationSettings; + noAggregationBarWidthSettings: TimeSeriesChartNoAggregationBarWidthSettings; +} + +export const timeSeriesChartDefaultSettings: TimeSeriesChartSettings = { + thresholds: [], + darkMode: false, + dataZoom: true, + stack: false, + yAxes: { + default: mergeDeep({} as TimeSeriesChartYAxisSettings, + defaultTimeSeriesChartYAxisSettings, + { id: 'default', order: 0 } as TimeSeriesChartYAxisSettings) + }, + xAxis: { + show: true, + label: '', + labelFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '600', + lineHeight: '1' + }, + labelColor: timeSeriesChartColorScheme['axis.label'].light, + position: AxisPosition.bottom, + showTickLabels: true, + tickLabelFont: { + family: 'Roboto', + size: 10, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '1' + }, + tickLabelColor: timeSeriesChartColorScheme['axis.tickLabel'].light, + showTicks: true, + ticksColor: timeSeriesChartColorScheme['axis.ticks'].light, + showLine: true, + lineColor: timeSeriesChartColorScheme['axis.line'].light, + showSplitLines: true, + splitLinesColor: timeSeriesChartColorScheme['axis.splitLine'].light + }, + animation: { + animation: true, + animationThreshold: 2000, + animationDuration: 1000, + animationEasing: TimeSeriesChartAnimationEasing.cubicOut, + animationDelay: 0, + animationDurationUpdate: 300, + animationEasingUpdate: TimeSeriesChartAnimationEasing.cubicOut, + animationDelayUpdate: 0 + }, + noAggregationBarWidthSettings: { + strategy: TimeSeriesChartNoAggregationBarWidthStrategy.group, + groupWidth: { + relative: true, + relativeWidth: 2, + absoluteWidth: 1000 + }, + barWidth: { + relative: true, + relativeWidth: 2, + absoluteWidth: 1000 + } + }, + showTooltip: true, + tooltipTrigger: EChartsTooltipTrigger.axis, + tooltipValueFont: { + family: 'Roboto', + size: 12, + sizeUnit: 'px', + style: 'normal', + weight: '500', + lineHeight: '16px' + }, + tooltipValueColor: 'rgba(0, 0, 0, 0.76)', + tooltipShowDate: true, + tooltipDateFormat: simpleDateFormat('dd MMM 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 +}; + +export interface SeriesFillSettings { + type: SeriesFillType; + opacity: number; + gradient: { + start: number; + end: number; + }; +} + +export interface LineSeriesSettings { + showLine: boolean; + step: boolean; + stepType: LineSeriesStepType; + smooth: boolean; + lineType: TimeSeriesChartLineType; + lineWidth: number; + showPoints: boolean; + showPointLabel: boolean; + pointLabelPosition: SeriesLabelPosition; + pointLabelFont: Font; + pointLabelColor: string; + pointShape: TimeSeriesChartShape; + pointSize: number; + fillAreaSettings: SeriesFillSettings; +} + +export interface BarSeriesSettings { + showBorder: boolean; + borderWidth: number; + borderRadius: number; + showLabel: boolean; + labelPosition: SeriesLabelPosition; + labelFont: Font; + labelColor: string; + backgroundSettings: SeriesFillSettings; +} + +export interface TimeSeriesChartKeySettings { + yAxisId: TimeSeriesChartYAxisId; + showInLegend: boolean; + dataHiddenByDefault: boolean; + type: TimeSeriesChartSeriesType; + lineSettings: LineSeriesSettings; + barSettings: BarSeriesSettings; +} + +export const timeSeriesChartKeyDefaultSettings: TimeSeriesChartKeySettings = { + yAxisId: 'default', + showInLegend: true, + dataHiddenByDefault: false, + type: TimeSeriesChartSeriesType.line, + lineSettings: { + showLine: true, + step: false, + stepType: LineSeriesStepType.start, + smooth: false, + lineType: TimeSeriesChartLineType.solid, + lineWidth: 2, + showPoints: false, + showPointLabel: false, + pointLabelPosition: SeriesLabelPosition.top, + pointLabelFont: { + family: 'Roboto', + size: 11, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '1' + }, + pointLabelColor: timeSeriesChartColorScheme['series.label'].light, + pointShape: TimeSeriesChartShape.emptyCircle, + pointSize: 4, + fillAreaSettings: { + type: SeriesFillType.none, + opacity: 0.4, + gradient: { + start: 100, + end: 0 + } + } + }, + barSettings: { + showBorder: false, + borderWidth: 2, + borderRadius: 0, + showLabel: false, + labelPosition: SeriesLabelPosition.top, + labelFont: { + family: 'Roboto', + size: 11, + sizeUnit: 'px', + style: 'normal', + weight: '400', + lineHeight: '1' + }, + labelColor: timeSeriesChartColorScheme['series.label'].light, + backgroundSettings: { + type: SeriesFillType.none, + opacity: 0.4, + gradient: { + start: 100, + end: 0 + } + } + } +}; + +export interface TimeSeriesChartDataItem extends EChartsSeriesItem { + yAxisId: TimeSeriesChartYAxisId; + yAxisIndex: number; + option?: LineSeriesOption | CustomSeriesOption; + barRenderContext?: BarRenderContext; +} + +type TimeSeriesChartThresholdValue = number | string | (number | string)[]; + +export interface TimeSeriesChartThresholdItem { + id: string; + yAxisId: TimeSeriesChartYAxisId; + yAxisIndex: number; + latestDataKey?: DataKey; + units?: string; + decimals?: number; + value: TimeSeriesChartThresholdValue; + settings: TimeSeriesChartThreshold; + option?: LineSeriesOption; +} + +export interface TimeSeriesChartYAxis { + id: string; + decimals: number; + settings: TimeSeriesChartYAxisSettings; + option: YAXisOption & ValueAxisBaseOption; + intervalCalculator?: (axis: Axis2D) => number; + ticksFormatter?: (value: any) => string; +} + +export const createTimeSeriesYAxis = (units: string, + decimals: number, + settings: TimeSeriesChartYAxisSettings, + darkMode: boolean): TimeSeriesChartYAxis => { + const yAxisTickLabelStyle = createChartTextStyle(settings.tickLabelFont, + settings.tickLabelColor, darkMode, 'axis.tickLabel'); + const yAxisNameStyle = createChartTextStyle(settings.labelFont, + settings.labelColor, darkMode, 'axis.label'); + let ticksFormatter: (value: any) => string; + if (settings.ticksFormatter && settings.ticksFormatter.length) { + ticksFormatter = parseFunction(settings.ticksFormatter, ['value']); + } + const yAxis: TimeSeriesChartYAxis = { + id: settings.id, + decimals, + settings, + option: { + show: settings.show, + type: 'value', + position: settings.position, + id: settings.id, + offset: 0, + alignTicks: true, + scale: true, + min: settings.min, + max: settings.max, + name: settings.label, + nameLocation: 'middle', + nameRotate: settings.position === AxisPosition.left ? 90 : -90, + nameTextStyle: { + color: yAxisNameStyle.color, + fontStyle: yAxisNameStyle.fontStyle, + fontWeight: yAxisNameStyle.fontWeight, + fontFamily: yAxisNameStyle.fontFamily, + fontSize: yAxisNameStyle.fontSize + }, + axisLine: { + show: settings.showLine, + onZero: false, + lineStyle: { + color: prepareChartThemeColor(settings.lineColor, darkMode, 'axis.line') + } + }, + axisTick: { + show: settings.showTicks, + lineStyle: { + color: prepareChartThemeColor(settings.ticksColor, darkMode, 'axis.ticks') + } + }, + axisLabel: { + show: settings.showTickLabels, + color: yAxisTickLabelStyle.color, + fontStyle: yAxisTickLabelStyle.fontStyle, + fontWeight: yAxisTickLabelStyle.fontWeight, + fontFamily: yAxisTickLabelStyle.fontFamily, + fontSize: yAxisTickLabelStyle.fontSize, + formatter: (value: any) => { + let result: string; + if (ticksFormatter) { + try { + result = ticksFormatter(value); + } catch (_e) {} + } + if (isUndefined(result)) { + result = formatValue(value, decimals, units, false); + } + return result; + } + }, + splitLine: { + show: settings.showSplitLines, + lineStyle: { + color: prepareChartThemeColor(settings.splitLinesColor, darkMode, 'axis.splitLine') + } + } + } + }; + if (settings.intervalCalculator && settings.intervalCalculator.length) { + yAxis.intervalCalculator = parseFunction(settings.intervalCalculator, ['axis']); + } + return yAxis; +}; + +export const createTimeSeriesXAxisOption = (settings: TimeSeriesChartAxisSettings, + min: number, max: number, darkMode: boolean): XAXisOption => { + const xAxisTickLabelStyle = createChartTextStyle(settings.tickLabelFont, + settings.tickLabelColor, darkMode, 'axis.tickLabel'); + const xAxisNameStyle = createChartTextStyle(settings.labelFont, + settings.labelColor, darkMode, 'axis.label'); + return { + show: settings.show, + type: 'time', + scale: true, + position: settings.position, + name: settings.label, + nameLocation: 'middle', + nameTextStyle: { + color: xAxisNameStyle.color, + fontStyle: xAxisNameStyle.fontStyle, + fontWeight: xAxisNameStyle.fontWeight, + fontFamily: xAxisNameStyle.fontFamily, + fontSize: xAxisNameStyle.fontSize + }, + axisTick: { + show: settings.showTicks, + lineStyle: { + color: prepareChartThemeColor(settings.ticksColor, darkMode, 'axis.ticks') + } + }, + axisLabel: { + show: settings.showTickLabels, + color: xAxisTickLabelStyle.color, + fontStyle: xAxisTickLabelStyle.fontStyle, + fontWeight: xAxisTickLabelStyle.fontWeight, + fontFamily: xAxisTickLabelStyle.fontFamily, + fontSize: xAxisTickLabelStyle.fontSize, + hideOverlap: true, + /* formatter: { + year: '{yyyy}', + month: '{MMM}', + day: '{d}', + hour: '{HH}:{mm}', + minute: '{HH}:{mm}', + second: '{HH}:{mm}:{ss}', + millisecond: '{hh}:{mm}:{ss} {SSS}' + } */ + }, + axisLine: { + show: settings.showLine, + onZero: false, + lineStyle: { + color: prepareChartThemeColor(settings.lineColor, darkMode, 'axis.line') + } + }, + splitLine: { + show: settings.showSplitLines, + lineStyle: { + color: prepareChartThemeColor(settings.splitLinesColor, darkMode, 'axis.splitLine') + } + }, + min, + max + }; +}; + +export const generateChartData = (dataItems: TimeSeriesChartDataItem[], + thresholdItems: TimeSeriesChartThresholdItem[], + stack: boolean, + noAggregation: boolean, + barRenderSharedContext: BarRenderSharedContext, + darkMode: boolean): Array => { + let series = generateChartSeries(dataItems, + stack, noAggregation, barRenderSharedContext, darkMode); + if (thresholdItems.length) { + const thresholds = generateChartThresholds(thresholdItems, darkMode); + series = series.concat(thresholds); + } + return series; +}; + +export const calculateThresholdsOffset = (chart: ECharts, + thresholdItems: TimeSeriesChartThresholdItem[], + yAxisList: TimeSeriesChartYAxis[]): [number, number] => { + const result: [number, number] = [0, 0]; + for (const item of thresholdItems) { + const yAxis = yAxisList[item.yAxisIndex]; + const offset = measureThresholdLabelOffset(chart, yAxis.id, item.id, item.value); + result[0] = Math.max(result[0], offset[0]); + result[1] = Math.max(result[1], offset[1]); + } + return result; +}; + +export const parseThresholdData = (value: any): TimeSeriesChartThresholdValue => { + let thresholdValue: TimeSeriesChartThresholdValue; + if (Array.isArray(value)) { + thresholdValue = value; + } else { + try { + const parsedData = JSON.parse(value); + thresholdValue = Array.isArray(parsedData) ? parsedData : [parsedData]; + } catch (e) { + thresholdValue = [value]; + } + } + return thresholdValue; +}; + +const generateChartThresholds = (thresholdItems: TimeSeriesChartThresholdItem[], darkMode: boolean): Array => { + const series: Array = []; + for (const item of thresholdItems) { + if (isDefinedAndNotNull(item.value)) { + let seriesOption = item.option; + if (!item.option) { + const thresholdLabelStyle = createChartTextStyle(item.settings.labelFont, + item.settings.labelColor, darkMode, 'threshold.label'); + seriesOption = { + type: 'line', + id: item.id, + dataGroupId: item.id, + yAxisIndex: item.yAxisIndex, + data: [], + tooltip: { + show: false + }, + markLine: { + lineStyle: { + width: item.settings.lineWidth, + color: prepareChartThemeColor(item.settings.lineColor, darkMode, 'threshold.line'), + type: item.settings.lineType + }, + label: { + show: item.settings.showLabel, + position: item.settings.labelPosition, + color: thresholdLabelStyle.color, + fontStyle: thresholdLabelStyle.fontStyle, + fontWeight: thresholdLabelStyle.fontWeight, + fontFamily: thresholdLabelStyle.fontFamily, + fontSize: thresholdLabelStyle.fontSize, + formatter: params => formatValue(params.value, item.decimals, + item.units, false) + }, + emphasis: { + disabled: true + } + } + }; + item.option = seriesOption; + } + seriesOption.markLine.data = []; + if (Array.isArray(item.value)) { + for (const val of item.value) { + seriesOption.markLine.data.push(createThresholdData(val, item)); + } + } else { + seriesOption.markLine.data.push(createThresholdData(item.value, item)); + } + series.push(seriesOption); + } + } + return series; +}; + +const createThresholdData = (val: string | number, item: TimeSeriesChartThresholdItem): MarkLine2DDataItemOption => [ + { + xAxis: 'min', + yAxis: val, + value: val, + symbol: item.settings.startSymbol, + symbolSize: item.settings.startSymbolSize + }, + { + xAxis: 'max', + yAxis: val, + value: val, + symbol: item.settings.endSymbol, + symbolSize: item.settings.endSymbolSize + } + ]; + +const generateChartSeries = (dataItems: TimeSeriesChartDataItem[], + stack: boolean, + noAggregation: boolean, + barRenderSharedContext: BarRenderSharedContext, + darkMode: boolean): Array => { + const series: Array = []; + const enabledDataItems = dataItems.filter(d => d.enabled); + const barDataItems = enabledDataItems.filter(d => + d.dataKey.settings.type === TimeSeriesChartSeriesType.bar && d.data.length); + let barsCount = barDataItems.length; + const barGroups: number[] = []; + if (stack) { + barDataItems.forEach(item => { + if (barGroups.indexOf(item.yAxisIndex) === -1) { + barGroups.push(item.yAxisIndex); + } + }); + barsCount = barGroups.length; + } + for (const item of enabledDataItems) { + if (item.dataKey.settings.type === TimeSeriesChartSeriesType.bar) { + if (!item.barRenderContext) { + item.barRenderContext = {noAggregation, + shared: barRenderSharedContext}; + } + item.barRenderContext.noAggregation = noAggregation; + item.barRenderContext.barsCount = barsCount; + item.barRenderContext.barIndex = stack ? barGroups.indexOf(item.yAxisIndex) : barDataItems.indexOf(item); + if (stack) { + const stackItems = enabledDataItems.filter(d => d.yAxisIndex === item.yAxisIndex); + item.barRenderContext.currentStackItems = stackItems; + item.barRenderContext.barStackIndex = stackItems.indexOf(item); + } + } + const seriesOption = createTimeSeriesChartSeries(item, stack, darkMode); + series.push(seriesOption); + } + return series; +}; + +export const updateDarkMode = (options: EChartsOption, settings: TimeSeriesChartSettings, + yAxisList: TimeSeriesChartYAxis[], + dataItems: TimeSeriesChartDataItem[], thresholdDataItems: TimeSeriesChartThresholdItem[], + darkMode: boolean): EChartsOption => { + options.darkMode = darkMode; + if (Array.isArray(options.yAxis)) { + for (let i = 0; i < options.yAxis.length; i++) { + const yAxis = options.yAxis[i]; + const yAxisSettings = yAxisList[i].settings; + yAxis.nameTextStyle.color = prepareChartThemeColor(yAxisSettings.labelColor, darkMode, 'axis.label'); + yAxis.axisLabel.color = prepareChartThemeColor(yAxisSettings.tickLabelColor, darkMode, 'axis.tickLabel'); + yAxis.axisLine.lineStyle.color = prepareChartThemeColor(yAxisSettings.lineColor, darkMode, 'axis.line'); + yAxis.axisTick.lineStyle.color = prepareChartThemeColor(yAxisSettings.ticksColor, darkMode, 'axis.ticks'); + yAxis.splitLine.lineStyle.color = prepareChartThemeColor(yAxisSettings.splitLinesColor, darkMode, 'axis.splitLine'); + } + } + if (Array.isArray(options.xAxis)) { + for (const xAxis of options.xAxis) { + xAxis.nameTextStyle.color = prepareChartThemeColor(settings.xAxis.labelColor, darkMode, 'axis.label'); + xAxis.axisLabel.color = prepareChartThemeColor(settings.xAxis.tickLabelColor, darkMode, 'axis.tickLabel'); + xAxis.axisLine.lineStyle.color = prepareChartThemeColor(settings.xAxis.lineColor, darkMode, 'axis.line'); + xAxis.axisTick.lineStyle.color = prepareChartThemeColor(settings.xAxis.ticksColor, darkMode, 'axis.ticks'); + xAxis.splitLine.lineStyle.color = prepareChartThemeColor(settings.xAxis.splitLinesColor, darkMode, 'axis.splitLine'); + } + } + for (const item of dataItems) { + if (item.dataKey.settings.type === TimeSeriesChartSeriesType.line) { + const lineSettings = item.dataKey.settings as LineSeriesSettings; + if (item.option.label?.show) { + item.option.label.rich.value.color = prepareChartThemeColor(lineSettings.pointLabelColor, darkMode, 'series.label'); + } + if (Array.isArray(options.series)) { + const series = options.series.find(s => s.id === item.id); + if (series) { + if (series.label?.show) { + series.label.rich.value.color = prepareChartThemeColor(lineSettings.pointLabelColor, darkMode, 'series.label'); + } + } + } + } else { + if (item.barRenderContext?.labelOption?.show) { + const barSettings = item.dataKey.settings as BarSeriesSettings; + item.barRenderContext.labelOption.rich.value.color = prepareChartThemeColor(barSettings.labelColor, + darkMode, 'series.label'); + } + } + } + for (const item of thresholdDataItems) { + if (Array.isArray(options.series)) { + const series = options.series.find(s => s.id === item.id); + if (series) { + series.markLine.lineStyle.color = prepareChartThemeColor(item.settings.lineColor, darkMode, 'threshold.line'); + if (series.markLine?.label?.show) { + series.markLine.label.color = prepareChartThemeColor(item.settings.labelColor, darkMode, 'threshold.label'); + } + } + } + } + return options; +}; + +const createTimeSeriesChartSeries = (item: TimeSeriesChartDataItem, + stack: boolean, + darkMode: boolean): LineSeriesOption | CustomSeriesOption => { + let seriesOption = item.option; + if (!seriesOption) { + const dataKey = item.dataKey; + const settings: TimeSeriesChartKeySettings = dataKey.settings; + const seriesColor = item.dataKey.color; + seriesOption = { + id: item.id, + dataGroupId: item.id, + yAxisIndex: item.yAxisIndex, + name: item.dataKey.label, + color: seriesColor, + stack: stack ? (item.yAxisIndex + '') : undefined, + stackStrategy: 'all', + emphasis: { + focus: 'series' + }, + dimensions: [ + {name: 'intervalStart', type: 'number'}, + {name: 'intervalEnd', type: 'number'} + ], + encode: { + intervalStart: 2, + intervalEnd: 3 + } + }; + item.option = seriesOption; + if (settings.type === TimeSeriesChartSeriesType.line) { + const lineSettings = settings.lineSettings; + const lineSeriesOption = seriesOption as LineSeriesOption; + lineSeriesOption.type = 'line'; + lineSeriesOption.label = createSeriesLabelOption(item, lineSettings.showPointLabel, + lineSettings.pointLabelFont, lineSettings.pointLabelColor, lineSettings.pointLabelPosition, darkMode); + lineSeriesOption.step = lineSettings.step ? lineSettings.stepType : false; + lineSeriesOption.smooth = lineSettings.smooth; + lineSeriesOption.lineStyle = { + width: lineSettings.showLine ? lineSettings.lineWidth : 0, + type: lineSettings.lineType + }; + if (lineSettings.fillAreaSettings.type !== SeriesFillType.none) { + lineSeriesOption.areaStyle = {}; + if (lineSettings.fillAreaSettings.type === SeriesFillType.opacity) { + lineSeriesOption.areaStyle.opacity = lineSettings.fillAreaSettings.opacity; + } else { + lineSeriesOption.areaStyle.opacity = 1; + lineSeriesOption.areaStyle.color = createLinearOpacityGradient(seriesColor, lineSettings.fillAreaSettings.gradient); + } + } + lineSeriesOption.showSymbol = lineSettings.showPoints; + lineSeriesOption.symbol = lineSettings.pointShape; + lineSeriesOption.symbolSize = lineSettings.pointSize; + } else { + const barSettings = settings.barSettings; + const barSeriesOption = (seriesOption as any) as CustomSeriesOption; + barSeriesOption.type = 'custom'; + const barVisualSettings: BarVisualSettings = { + color: seriesColor, + borderColor: seriesColor, + borderWidth: barSettings.showBorder ? barSettings.borderWidth : 0, + borderRadius: barSettings.borderRadius + }; + if (barSettings.backgroundSettings.type === SeriesFillType.none) { + barVisualSettings.color = seriesColor; + } else if (barSettings.backgroundSettings.type === SeriesFillType.opacity) { + barVisualSettings.color = tinycolor(seriesColor).setAlpha(barSettings.backgroundSettings.opacity).toRgbString(); + } else { + barVisualSettings.color = createLinearOpacityGradient(seriesColor, barSettings.backgroundSettings.gradient); + } + item.barRenderContext.visualSettings = barVisualSettings; + item.barRenderContext.labelOption = createSeriesLabelOption(item, barSettings.showLabel, + barSettings.labelFont, barSettings.labelColor, barSettings.labelPosition, darkMode); + barSeriesOption.renderItem = (params, api) => + renderTimeSeriesBar(params, api, item.barRenderContext); + } + } + seriesOption.data = item.data; + return seriesOption; +}; + +const createSeriesLabelOption = (item: TimeSeriesChartDataItem, show: boolean, + labelFont: Font, labelColor: string, position: SeriesLabelPosition, + darkMode: boolean): SeriesLabelOption => { + let labelStyle: ComponentStyle = {}; + if (show) { + labelStyle = createChartTextStyle(labelFont, labelColor, darkMode, 'series.label'); + } + return { + show, + position, + formatter: (params): string => { + const value = formatValue(params.value[1], item.decimals, item.units, false); + return `{value|${value}}`; + }, + rich: { + value: labelStyle + } + }; +}; + +const createChartTextStyle = (font: Font, color: string, darkMode: boolean, colorKey?: string): ComponentStyle => { + const style = textStyle(font); + delete style.lineHeight; + style.fontSize = font.size; + style.color = prepareChartThemeColor(color, darkMode, colorKey); + return style; +}; + +const createLinearOpacityGradient = (color: string, gradient: {start: number; end: number}): LinearGradientObject => ({ + type: 'linear', + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [{ + offset: 0, color: tinycolor(color).setAlpha(gradient.start / 100).toRgbString() // color at 0% + }, { + offset: 1, color: tinycolor(color).setAlpha(gradient.end / 100).toRgbString() // color at 100% + }], + global: false +}); + +const prepareChartThemeColor = (color: string, darkMode: boolean, colorKey?: string): string => { + if (darkMode) { + let colorInstance = tinycolor(color); + if (colorInstance.isDark()) { + if (colorKey && timeSeriesChartColorScheme[colorKey]) { + return timeSeriesChartColorScheme[colorKey].dark; + } else { + const rgb = colorInstance.toRgb(); + colorInstance = tinycolor({r: 255 - rgb.r, g: 255 - rgb.g, b: 255 - rgb.b, a: rgb.a}); + return colorInstance.toRgbString(); + } + } + } + return color; +}; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts new file mode 100644 index 0000000000..57e52f5f5d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/chart/time-series-chart.ts @@ -0,0 +1,772 @@ +/// +/// 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. +/// + +import { WidgetContext } from '@home/models/widget-component.models'; +import { + AxisPosition, + calculateThresholdsOffset, + createTimeSeriesXAxisOption, + createTimeSeriesYAxis, + defaultTimeSeriesChartYAxisSettings, + generateChartData, + parseThresholdData, + SeriesLabelPosition, + TimeSeriesChartDataItem, + timeSeriesChartDefaultSettings, + timeSeriesChartKeyDefaultSettings, + TimeSeriesChartKeySettings, TimeSeriesChartNoAggregationBarWidthStrategy, + TimeSeriesChartSeriesType, + TimeSeriesChartSettings, + TimeSeriesChartShape, + TimeSeriesChartThreshold, + timeSeriesChartThresholdDefaultSettings, + TimeSeriesChartThresholdItem, + TimeSeriesChartThresholdType, + TimeSeriesChartType, + TimeSeriesChartYAxis, + TimeSeriesChartYAxisId, + TimeSeriesChartYAxisSettings, + updateDarkMode +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { ResizeObserver } from '@juggle/resize-observer'; +import { + calculateXAxisHeight, + calculateYAxisWidth, + ECharts, + echartsModule, + EChartsOption, + echartsTooltipFormatter, + EChartsTooltipTrigger, + getAxisExtent, + getYAxis, + measureXAxisNameHeight, + measureYAxisNameWidth, + toNamedData +} from '@home/components/widget/lib/chart/echarts-widget.models'; +import { DateFormatProcessor } from '@shared/models/widget-settings.models'; +import { isDefinedAndNotNull, isEqual, mergeDeep } from '@core/utils'; +import { DataKey, Datasource, DatasourceType, widgetType } from '@shared/models/widget.models'; +import * as echarts from 'echarts/core'; +import { CallbackDataParams } from 'echarts/types/dist/shared'; +import { Renderer2 } from '@angular/core'; +import { CustomSeriesOption, LineSeriesOption } from 'echarts/charts'; +import { BehaviorSubject } from 'rxjs'; +import { AggregationType } from '@shared/models/time/time.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { WidgetSubscriptionOptions } from '@core/api/widget-api.models'; +import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; +import { DeepPartial } from '@shared/models/common'; +import { BarRenderSharedContext } from '@home/components/widget/lib/chart/time-series-chart-bar.models'; + +export class TbTimeSeriesChart { + + public static dataKeySettings(type = TimeSeriesChartType.default): DataKeySettingsFunction { + return (key, isLatestDataKey) => { + if (!isLatestDataKey) { + const settings = mergeDeep({} as TimeSeriesChartKeySettings, + timeSeriesChartKeyDefaultSettings); + if (type === TimeSeriesChartType.line) { + settings.type = TimeSeriesChartSeriesType.line; + } else if (type === TimeSeriesChartType.bar) { + settings.type = TimeSeriesChartSeriesType.bar; + } else if (type === TimeSeriesChartType.point) { + settings.type = TimeSeriesChartSeriesType.line; + settings.lineSettings.showLine = false; + settings.lineSettings.showPoints = true; + settings.lineSettings.pointShape = TimeSeriesChartShape.circle; + settings.lineSettings.pointSize = 8; + } + return settings; + } + return null; + }; + } + + private get noAggregation(): boolean { + return this.ctx.defaultSubscription.timeWindowConfig?.aggregation?.type === AggregationType.NONE; + } + + private readonly shapeResize$: ResizeObserver; + + private readonly settings: TimeSeriesChartSettings; + + private yAxisList: TimeSeriesChartYAxis[] = []; + private dataItems: TimeSeriesChartDataItem[] = []; + private thresholdItems: TimeSeriesChartThresholdItem[] = []; + + private timeSeriesChart: ECharts; + private timeSeriesChartOptions: EChartsOption; + + private readonly tooltipDateFormat: DateFormatProcessor; + + private yMinSubject = new BehaviorSubject(-1); + private yMaxSubject = new BehaviorSubject(1); + + private darkMode = false; + + private messageChannel = new BroadcastChannel('tbMessageChannel'); + + private topPointLabels = false; + + private componentIndexCounter = 0; + + private highlightedDataKey: DataKey; + + private barRenderSharedContext: BarRenderSharedContext; + + yMin$ = this.yMinSubject.asObservable(); + yMax$ = this.yMaxSubject.asObservable(); + + constructor(private ctx: WidgetContext, + private readonly inputSettings: DeepPartial, + private chartElement: HTMLElement, + private renderer: Renderer2, + private autoResize = true) { + + this.settings = mergeDeep({} as TimeSeriesChartSettings, + timeSeriesChartDefaultSettings, + this.inputSettings as TimeSeriesChartSettings); + this.darkMode = this.settings.darkMode; + this.setupYAxes(); + this.setupData(); + this.setupThresholds(); + if (this.settings.showTooltip && this.settings.tooltipShowDate) { + this.tooltipDateFormat = DateFormatProcessor.fromSettings(this.ctx.$injector, this.settings.tooltipDateFormat); + } + this.onResize(); + if (this.autoResize) { + this.shapeResize$ = new ResizeObserver(() => { + this.onResize(); + }); + this.shapeResize$.observe(this.chartElement); + } + this.messageChannel.addEventListener('message', (event) => { + if (event?.data?.type === 'tbDarkMode') { + const darkMode = !!event?.data?.darkMode; + this.setDarkMode(darkMode); + } + }); + } + + public update(): void { + for (const item of this.dataItems) { + const datasourceData = this.ctx.data ? this.ctx.data.find(d => d.dataKey === item.dataKey) : null; + if (!isEqual(item.dataSet, datasourceData?.data)) { + item.dataSet = datasourceData?.data; + item.data = datasourceData?.data ? toNamedData(datasourceData.data) : []; + } + } + this.onResize(); + if (this.timeSeriesChart) { + this.timeSeriesChartOptions.xAxis[0].min = this.ctx.defaultSubscription.timeWindow.minTime; + this.timeSeriesChartOptions.xAxis[0].max = this.ctx.defaultSubscription.timeWindow.maxTime; + this.timeSeriesChartOptions.xAxis[0].tbTimeWindow = this.ctx.defaultSubscription.timeWindow; + if (this.noAggregation) { + this.timeSeriesChartOptions.tooltip[0].axisPointer.type = 'line'; + } else { + this.timeSeriesChartOptions.tooltip[0].axisPointer.type = 'shadow'; + } + this.barRenderSharedContext.timeInterval = this.ctx.timeWindow.interval; + this.updateSeriesData(true); + if (this.highlightedDataKey) { + this.keyEnter(this.highlightedDataKey); + } + } + } + + public latestUpdated() { + let update = false; + if (this.ctx.latestData) { + for (const item of this.thresholdItems) { + if (item.settings.type === TimeSeriesChartThresholdType.latestKey && item.latestDataKey) { + const data = this.ctx.latestData.find(d => d.dataKey === item.latestDataKey); + if (data.data[0]) { + item.value = parseThresholdData(data.data[0][1]); + update = true; + } + } + } + } + if (this.timeSeriesChart && update) { + this.updateSeriesData(); + } + } + + public keyEnter(dataKey: DataKey): void { + this.highlightedDataKey = dataKey; + const item = this.dataItems.find(d => d.dataKey === dataKey); + if (item) { + this.timeSeriesChart.dispatchAction({ + type: 'highlight', + seriesId: item.id + }); + } + } + + public keyLeave(dataKey: DataKey): void { + this.highlightedDataKey = null; + const item = this.dataItems.find(d => d.dataKey === dataKey); + if (item) { + this.timeSeriesChart.dispatchAction({ + type: 'downplay', + seriesId: item.id + }); + } + } + + public toggleKey(dataKey: DataKey): void { + const enable = dataKey.hidden; + const dataItem = this.dataItems.find(d => d.dataKey === dataKey); + if (dataItem) { + dataItem.enabled = enable; + if (!enable) { + this.timeSeriesChart.dispatchAction({ + type: 'downplay', + seriesId: dataItem.id + }); + } + this.timeSeriesChartOptions.series = this.updateSeries(); + const mergeList = ['series']; + if (this.updateYAxisScale(this.yAxisList)) { + this.timeSeriesChartOptions.yAxis = this.yAxisList.map(axis => axis.option); + mergeList.push('yAxis'); + } + this.timeSeriesChart.setOption(this.timeSeriesChartOptions, this.settings.stack ? {notMerge: true} : {replaceMerge: mergeList}); + this.updateAxes(); + dataKey.hidden = !enable; + if (enable) { + this.timeSeriesChart.dispatchAction({ + type: 'highlight', + seriesId: dataItem.id + }); + } + } + } + + public destroy(): void { + if (this.shapeResize$) { + this.shapeResize$.disconnect(); + } + if (this.timeSeriesChart) { + this.timeSeriesChart.dispose(); + } + this.yMinSubject.complete(); + this.yMaxSubject.complete(); + this.messageChannel.close(); + } + + public resize(): void { + this.onResize(); + } + + public setDarkMode(darkMode: boolean): void { + if (this.darkMode !== darkMode) { + this.darkMode = darkMode; + if (this.timeSeriesChart) { + this.timeSeriesChartOptions = updateDarkMode(this.timeSeriesChartOptions, + this.settings, this.yAxisList, this.dataItems, + this.thresholdItems, darkMode); + this.timeSeriesChart.setOption(this.timeSeriesChartOptions); + } + } + } + + public isDarkMode(): boolean { + return this.darkMode; + } + + private setupData(): void { + const noAggregationBarWidthSettings = this.settings.noAggregationBarWidthSettings; + const targetBarWidth = noAggregationBarWidthSettings.strategy === TimeSeriesChartNoAggregationBarWidthStrategy.group ? + noAggregationBarWidthSettings.groupWidth : noAggregationBarWidthSettings.barWidth; + this.barRenderSharedContext = { + timeInterval: this.ctx.timeWindow?.interval, + noAggregationBarWidthStrategy: noAggregationBarWidthSettings.strategy, + noAggregationWidthRelative: targetBarWidth.relative, + noAggregationWidth: targetBarWidth.relative ? targetBarWidth.relativeWidth : targetBarWidth.absoluteWidth + }; + if (this.ctx.datasources.length) { + for (const datasource of this.ctx.datasources) { + const dataKeys = datasource.dataKeys; + for (const dataKey of dataKeys) { + const keySettings = mergeDeep({} as TimeSeriesChartKeySettings, + timeSeriesChartKeyDefaultSettings, dataKey.settings); + if ((keySettings.type === TimeSeriesChartSeriesType.line && keySettings.lineSettings.showPointLabel && + keySettings.lineSettings.pointLabelPosition === SeriesLabelPosition.top) || + (keySettings.type === TimeSeriesChartSeriesType.bar && keySettings.barSettings.showLabel)) { + this.topPointLabels = true; + } + dataKey.settings = keySettings; + const datasourceData = this.ctx.data ? this.ctx.data.find(d => d.dataKey === dataKey) : null; + const namedData = datasourceData?.data ? toNamedData(datasourceData.data) : []; + const units = dataKey.units && dataKey.units.length ? dataKey.units : this.ctx.units; + const decimals = isDefinedAndNotNull(dataKey.decimals) ? dataKey.decimals : + (isDefinedAndNotNull(this.ctx.decimals) ? this.ctx.decimals : 2); + let yAxisId = keySettings.yAxisId; + if (!Object.keys(this.settings.yAxes).includes(yAxisId)) { + yAxisId = 'default'; + } + this.dataItems.push({ + id: this.nextComponentId(), + units, + decimals, + yAxisId, + yAxisIndex: this.getYAxisIndex(yAxisId), + dataKey, + data: namedData, + enabled: !keySettings.dataHiddenByDefault + }); + } + } + } + } + + private setupThresholds(): void { + const thresholdDatasources: Datasource[] = []; + for (const thresholdSettings of this.settings.thresholds) { + const threshold = mergeDeep({} as TimeSeriesChartThreshold, + timeSeriesChartThresholdDefaultSettings, thresholdSettings); + let latestDataKey: DataKey = null; + let entityDataKey: DataKey = null; + let value = null; + if (threshold.type === TimeSeriesChartThresholdType.latestKey) { + if (this.ctx.datasources.length) { + for (const datasource of this.ctx.datasources) { + latestDataKey = datasource.latestDataKeys?.find(d => + (d.type === DataKeyType.function && d.label === threshold.latestKey) || + (d.type !== DataKeyType.function && d.name === threshold.latestKey && + d.type === threshold.latestKeyType)); + if (latestDataKey) { + break; + } + } + } + if (!latestDataKey) { + continue; + } + } else if (threshold.type === TimeSeriesChartThresholdType.entity) { + const entityAliasId = this.ctx.aliasController.getEntityAliasId(threshold.entityAlias); + if (!entityAliasId) { + continue; + } + let datasource = thresholdDatasources.find(d => d.entityAliasId === entityAliasId); + entityDataKey = { + type: threshold.entityKeyType, + name: threshold.entityKey, + label: threshold.entityKey, + settings: {} + }; + if (datasource) { + datasource.dataKeys.push(entityDataKey); + } + datasource = { + type: DatasourceType.entity, + name: threshold.entityAlias, + aliasName: threshold.entityAlias, + entityAliasId, + dataKeys: [ entityDataKey ] + }; + thresholdDatasources.push(datasource); + } else { // constant + value = threshold.value; + } + const units = threshold.units && threshold.units.length ? threshold.units : this.ctx.units; + const decimals = isDefinedAndNotNull(threshold.decimals) ? threshold.decimals : + (isDefinedAndNotNull(this.ctx.decimals) ? this.ctx.decimals : 2); + let yAxisId = threshold.yAxisId; + if (!Object.keys(this.settings.yAxes).includes(yAxisId)) { + yAxisId = 'default'; + } + const thresholdItem: TimeSeriesChartThresholdItem = { + id: this.nextComponentId(), + units, + decimals, + yAxisId, + yAxisIndex: this.getYAxisIndex(yAxisId), + value, + latestDataKey, + settings: threshold + }; + if (entityDataKey) { + entityDataKey.settings.thresholdItemId = thresholdItem.id; + } + this.thresholdItems.push(thresholdItem); + } + this.subscribeForEntityThresholds(thresholdDatasources); + } + + private setupYAxes(): void { + const yAxisSettingsList = Object.values(this.settings.yAxes); + yAxisSettingsList.sort((a1, a2) => a1.order - a2.order); + for (const yAxisSettings of yAxisSettingsList) { + const axisSettings = mergeDeep({} as TimeSeriesChartYAxisSettings, + defaultTimeSeriesChartYAxisSettings, yAxisSettings); + const units = axisSettings.units && axisSettings.units.length ? axisSettings.units : this.ctx.units; + const decimals = isDefinedAndNotNull(axisSettings.decimals) ? axisSettings.decimals : + (isDefinedAndNotNull(this.ctx.decimals) ? this.ctx.decimals : 2); + const yAxis = createTimeSeriesYAxis(units, decimals, axisSettings, this.darkMode); + this.yAxisList.push(yAxis); + } + } + + + private nextComponentId(): string { + return (this.componentIndexCounter++) + ''; + } + + private getYAxisIndex(id: TimeSeriesChartYAxisId): number { + let yAxisIndex = this.yAxisList.findIndex(axis => axis.id === id); + if (yAxisIndex === -1) { + yAxisIndex = this.yAxisList.findIndex(axis => axis.id === 'default'); + if (yAxisIndex === -1 && this.yAxisList.length) { + yAxisIndex = 0; + } + } + return yAxisIndex; + } + + private subscribeForEntityThresholds(datasources: Datasource[]) { + if (datasources.length) { + const thresholdsSourcesSubscriptionOptions: WidgetSubscriptionOptions = { + datasources, + useDashboardTimewindow: false, + type: widgetType.latest, + callbacks: { + onDataUpdated: (subscription) => { + let update = false; + if (subscription.data) { + for (const item of this.thresholdItems) { + if (item.settings.type === TimeSeriesChartThresholdType.entity) { + const data = subscription.data.find(d => d.dataKey.settings?.thresholdItemId === item.id); + if (data.data[0]) { + item.value = parseThresholdData(data.data[0][1]); + update = true; + } + } + } + } + if (this.timeSeriesChart && update) { + this.updateSeriesData(); + } + } + } + }; + this.ctx.subscriptionApi.createSubscription(thresholdsSourcesSubscriptionOptions, true).subscribe(); + } + } + + private drawChart() { + echartsModule.init(); + this.timeSeriesChart = echarts.init(this.chartElement, null, { + renderer: 'canvas' + }); + this.timeSeriesChartOptions = { + darkMode: this.darkMode, + backgroundColor: 'transparent', + tooltip: [{ + trigger: this.settings.tooltipTrigger === EChartsTooltipTrigger.axis ? 'axis' : 'item', + confine: true, + appendTo: 'body', + axisPointer: { + type: this.noAggregation ? 'line' : 'shadow' + }, + formatter: (params: CallbackDataParams[]) => + this.settings.showTooltip ? echartsTooltipFormatter(this.renderer, this.tooltipDateFormat, + this.settings, params, 0, '', -1, this.dataItems) : undefined, + padding: [8, 12], + backgroundColor: this.settings.tooltipBackgroundColor, + borderWidth: 0, + extraCssText: `line-height: 1; backdrop-filter: blur(${this.settings.tooltipBackgroundBlur}px);` + }], + grid: [{ + top: this.minTopOffset(), + left: this.settings.dataZoom ? 5 : 0, + right: this.settings.dataZoom ? 5 : 0, + bottom: this.minBottomOffset() + }], + xAxis: [ + createTimeSeriesXAxisOption(this.settings.xAxis, this.ctx.defaultSubscription.timeWindow.minTime, + this.ctx.defaultSubscription.timeWindow.maxTime, this.darkMode) + ], + yAxis: this.yAxisList.map(axis => axis.option), + dataZoom: [ + { + type: 'inside', + disabled: !this.settings.dataZoom, + realtime: true + }, + { + type: 'slider', + show: this.settings.dataZoom, + showDetail: false, + realtime: true, + bottom: 10 + } + ], + animation: this.settings.animation.animation, + animationThreshold: this.settings.animation.animationThreshold, + animationDuration: this.settings.animation.animationDuration, + animationEasing: this.settings.animation.animationEasing, + animationDelay: this.settings.animation.animationDelay, + animationDurationUpdate: this.settings.animation.animationDurationUpdate, + animationEasingUpdate: this.settings.animation.animationEasingUpdate, + animationDelayUpdate: this.settings.animation.animationDelayUpdate + }; + + this.timeSeriesChartOptions.xAxis[0].tbTimeWindow = this.ctx.defaultSubscription.timeWindow; + + this.timeSeriesChartOptions.series = this.updateSeries(); + if (this.updateYAxisScale(this.yAxisList)) { + this.timeSeriesChartOptions.yAxis = this.yAxisList.map(axis => axis.option); + } + + this.timeSeriesChart.setOption(this.timeSeriesChartOptions); + this.updateAxes(); + + if (this.settings.dataZoom) { + this.timeSeriesChart.on('datazoom', () => { + this.updateAxes(); + }); + } + } + + private updateSeriesData(updateScale = false): void { + this.timeSeriesChartOptions.series = this.updateSeries(); + if (updateScale && this.updateYAxisScale(this.yAxisList)) { + this.timeSeriesChartOptions.yAxis = this.yAxisList.map(axis => axis.option); + } + this.timeSeriesChart.setOption(this.timeSeriesChartOptions); + this.updateAxes(); + } + + private updateSeries(): Array { + return generateChartData(this.dataItems, this.thresholdItems, + this.settings.stack, + this.noAggregation, + this.barRenderSharedContext, this.darkMode); + } + + private updateAxes() { + const leftAxisList = this.yAxisList.filter(axis => axis.option.position === 'left'); + let res = this.updateYAxisOffset(leftAxisList); + let leftOffset = res.offset + (!res.offset && this.settings.dataZoom ? 5 : 0); + let changed = res.changed; + const rightAxisList = this.yAxisList.filter(axis => axis.option.position === 'right'); + res = this.updateYAxisOffset(rightAxisList); + let rightOffset = res.offset + (!res.offset && this.settings.dataZoom ? 5 : 0); + changed = changed || res.changed; + let bottomOffset = this.minBottomOffset(); + const minTopOffset = this.minTopOffset(); + let topOffset = minTopOffset; + if (this.timeSeriesChartOptions.xAxis[0].show) { + const xAxisHeight = calculateXAxisHeight(this.timeSeriesChart); + if (this.timeSeriesChartOptions.xAxis[0].position === AxisPosition.bottom) { + bottomOffset += xAxisHeight; + } else { + topOffset = Math.max(minTopOffset, xAxisHeight); + } + if (this.settings.xAxis.label) { + const nameHeight = measureXAxisNameHeight(this.timeSeriesChart, this.timeSeriesChartOptions.xAxis[0].name); + if (this.timeSeriesChartOptions.xAxis[0].position === AxisPosition.bottom) { + bottomOffset += nameHeight; + } else { + topOffset = Math.max(minTopOffset, xAxisHeight + nameHeight); + } + const nameGap = xAxisHeight; + if (this.timeSeriesChartOptions.xAxis[0].nameGap !== nameGap) { + this.timeSeriesChartOptions.xAxis[0].nameGap = nameGap; + changed = true; + } + } + } + + const thresholdsOffset = calculateThresholdsOffset(this.timeSeriesChart, this.thresholdItems, this.yAxisList); + leftOffset = Math.max(leftOffset, thresholdsOffset[0]); + rightOffset = Math.max(rightOffset, thresholdsOffset[1]); + + if (this.timeSeriesChartOptions.grid[0].left !== leftOffset || + this.timeSeriesChartOptions.grid[0].right !== rightOffset || + this.timeSeriesChartOptions.grid[0].bottom !== bottomOffset || + this.timeSeriesChartOptions.grid[0].top !== topOffset) { + this.timeSeriesChartOptions.grid[0].left = leftOffset; + this.timeSeriesChartOptions.grid[0].right = rightOffset; + this.timeSeriesChartOptions.grid[0].bottom = bottomOffset; + this.timeSeriesChartOptions.grid[0].top = topOffset; + changed = true; + } + if (changed) { + this.timeSeriesChartOptions.yAxis = this.yAxisList.map(axis => axis.option); + this.timeSeriesChart.setOption(this.timeSeriesChartOptions, {replaceMerge: ['yAxis', 'xAxis', 'grid'], lazyUpdate: true}); + } + changed = this.calculateYAxisInterval(this.yAxisList); + if (changed) { + this.timeSeriesChartOptions.yAxis = this.yAxisList.map(axis => axis.option); + this.timeSeriesChart.setOption(this.timeSeriesChartOptions, {replaceMerge: ['yAxis'], lazyUpdate: true}); + } + if (this.yAxisList.length) { + const extent = getAxisExtent(this.timeSeriesChart, this.yAxisList[0].id); + const min = extent[0]; + const max = extent[1]; + if (this.yMinSubject.value !== min) { + this.yMinSubject.next(min); + } + if (this.yMaxSubject.value !== max) { + this.yMaxSubject.next(max); + } + } + } + + private updateYAxisScale(axisList: TimeSeriesChartYAxis[]): boolean { + let changed = false; + for (const yAxis of axisList) { + const scaleYAxis = this.scaleYAxis(yAxis); + if (yAxis.option.scale !== scaleYAxis) { + yAxis.option.scale = scaleYAxis; + changed = true; + } + } + return changed; + } + + private updateYAxisOffset(axisList: TimeSeriesChartYAxis[]): {offset: number; changed: boolean} { + const result = {offset: 0, changed: false}; + let width = 0; + for (const yAxis of axisList) { + const newWidth = calculateYAxisWidth(this.timeSeriesChart, yAxis.id); + if (width && newWidth) { + result.offset += 5; + } + width = newWidth; + const showLine = !!width && yAxis.settings.showLine; + if (yAxis.option.axisLine.show !== showLine) { + yAxis.option.axisLine.show = showLine; + result.changed = true; + } + if (yAxis.option.offset !== result.offset) { + yAxis.option.offset = result.offset; + result.changed = true; + } + if (yAxis.settings.label) { + if (!width) { + if (yAxis.option.name) { + yAxis.option.name = null; + result.changed = true; + } + } else { + if (!yAxis.option.name) { + yAxis.option.name = yAxis.settings.label; + result.changed = true; + } + const nameGap = width; + if (yAxis.option.nameGap !== nameGap) { + yAxis.option.nameGap = nameGap; + result.changed = true; + } + const nameWidth = measureYAxisNameWidth(this.timeSeriesChart, yAxis.id, yAxis.settings.label); + result.offset += nameWidth; + } + } + result.offset += width; + } + return result; + } + + private scaleYAxis(yAxis: TimeSeriesChartYAxis): boolean { + const axisBarDataItems = this.dataItems.filter(d => d.yAxisId === yAxis.id && d.enabled && + d.data.length && d.dataKey.settings.type === TimeSeriesChartSeriesType.bar); + return !axisBarDataItems.length; + } + + private yAxisEmpty(yAxis: TimeSeriesChartYAxis): boolean { + const axisDataItems = this.dataItems.filter(d => d.yAxisId === yAxis.id && d.enabled && + d.data.length); + return !axisDataItems.length; + } + + private calculateYAxisInterval(axisList: TimeSeriesChartYAxis[]): boolean { + let changed = false; + for (const yAxis of axisList) { + const minInterval = this.yAxisEmpty(yAxis) ? undefined : (1 / Math.pow(10, yAxis.decimals)); + if (yAxis.option.minInterval !== minInterval) { + yAxis.option.minInterval = minInterval; + changed = true; + } + if (yAxis.intervalCalculator) { + const axis = getYAxis(this.timeSeriesChart, yAxis.id); + if (axis) { + try { + const interval = yAxis.intervalCalculator(axis); + if ((yAxis.option as any).interval !== interval) { + (yAxis.option as any).interval = interval; + changed = true; + } + } catch (_e) {} + } + } + } + return changed; + } + + private minTopOffset(): number { + const showTickLabels = + !!this.yAxisList.find(yAxis => yAxis.settings.show && yAxis.settings.showTickLabels); + return (this.topPointLabels) ? 20 : + (showTickLabels ? 10 : 5); + } + + private minBottomOffset(): number { + return this.settings.dataZoom ? 45 : 5; + } + + private onResize() { + const shapeWidth = this.chartElement.offsetWidth; + const shapeHeight = this.chartElement.offsetHeight; + if (shapeWidth && shapeHeight) { + if (!this.timeSeriesChart) { + this.drawChart(); + } else { + const width = this.timeSeriesChart.getWidth(); + const height = this.timeSeriesChart.getHeight(); + if (width !== shapeWidth || height !== shapeHeight) { + let barItems: TimeSeriesChartDataItem[]; + if (this.animationEnabled()) { + barItems = + this.dataItems.filter(d => d.enabled && d.data.length && + d.dataKey.settings.type === TimeSeriesChartSeriesType.bar); + this.updateBarsAnimation(barItems, false); + } + this.timeSeriesChart.resize(); + if (this.animationEnabled()) { + this.updateBarsAnimation(barItems, true); + } + } + } + } + } + + private animationEnabled(): boolean { + return this.settings.animation.animation; + } + + private updateBarsAnimation(barItems: TimeSeriesChartDataItem[], animation: boolean) { + if (barItems.length) { + barItems.forEach(item => { + item.option.animation = animation; + }); + this.timeSeriesChart.setOption(this.timeSeriesChartOptions); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts index 846c6b8331..2c46864388 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/flot-widget.ts @@ -206,8 +206,8 @@ export class TbFlot { if (this.settings.yaxis) { this.yaxis.font.color = this.settings.yaxis.color || this.yaxis.font.color; - this.yaxis.min = isDefined(this.settings.yaxis.min) ? this.settings.yaxis.min : null; - this.yaxis.max = isDefined(this.settings.yaxis.max) ? this.settings.yaxis.max : null; + this.yaxis.min = isNumber(this.settings.yaxis.min) ? this.settings.yaxis.min : null; + this.yaxis.max = isNumber(this.settings.yaxis.max) ? this.settings.yaxis.max : null; this.yaxis.label = this.utils.customTranslation(this.settings.yaxis.title, this.settings.yaxis.title) || null; this.yaxis.labelFont.color = this.yaxis.font.color; this.yaxis.labelFont.size = this.yaxis.font.size + 2; @@ -895,8 +895,8 @@ export class TbFlot { tickSize = yaxis.tickSize; } const position = keySettings.axisPosition && keySettings.axisPosition.length ? keySettings.axisPosition : 'left'; - const min = isDefined(keySettings.axisMin) ? keySettings.axisMin : yaxis.min; - const max = isDefined(keySettings.axisMax) ? keySettings.axisMax : yaxis.max; + const min = isNumber(keySettings.axisMin) ? keySettings.axisMin : yaxis.min; + const max = isNumber(keySettings.axisMax) ? keySettings.axisMax : yaxis.max; yaxis.label = label; yaxis.min = min; yaxis.max = max; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts index a760d1ece1..a46e6a8427 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/maps/map-widget2.ts @@ -298,6 +298,6 @@ export class MapWidgetController implements MapWidgetInterface { } } -export let TbMapWidgetV2: MapWidgetStaticInterface = MapWidgetController; +export const TbMapWidgetV2: MapWidgetStaticInterface = MapWidgetController; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html index 443ee4abc5..e40470d75e 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/bar-chart-with-labels-widget-settings.component.html @@ -124,6 +124,13 @@
+
+ +
+ {{ 'tooltip.show-date-time-interval' | translate }} +
+
+
{{ 'tooltip.background-color' | translate }}
+
+ +
+ {{ 'tooltip.show-date-time-interval' | translate }} +
+
+
{{ 'tooltip.background-color' | translate }}
+ +
+ + {{ 'widgets.time-series-chart.series.bar.show-border' | translate }} + +
+
+
widgets.time-series-chart.series.bar.border-width
+ + + +
+
+
widgets.time-series-chart.series.bar.border-radius
+ + + +
+
+ +
+ {{ 'widgets.time-series-chart.series.bar.label' | translate }} +
+
+
+ + + + {{ seriesLabelPositionTranslations.get(position) | translate }} + + + + + + + +
+
+ + +
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component.ts new file mode 100644 index 0000000000..b42cec925a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component.ts @@ -0,0 +1,148 @@ +/// +/// 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. +/// + +import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { + BarSeriesSettings, + seriesLabelPositions, + seriesLabelPositionTranslations +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { merge } from 'rxjs'; +import { formatValue, isDefinedAndNotNull } from '@core/utils'; +import { DataKeyConfigComponent } from '@home/components/widget/config/data-key-config.component'; + +@Component({ + selector: 'tb-time-series-chart-bar-settings', + templateUrl: './time-series-chart-bar-settings.component.html', + styleUrls: ['./../widget-settings.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TimeSeriesChartBarSettingsComponent), + multi: true + } + ] +}) +export class TimeSeriesChartBarSettingsComponent implements OnInit, ControlValueAccessor { + + seriesLabelPositions = seriesLabelPositions; + + seriesLabelPositionTranslations = seriesLabelPositionTranslations; + + labelPreviewFn = this._labelPreviewFn.bind(this); + + @Input() + disabled: boolean; + + private modelValue: BarSeriesSettings; + + private propagateChange = null; + + public barSettingsFormGroup: UntypedFormGroup; + + constructor(protected store: Store, + private dataKeyConfigComponent: DataKeyConfigComponent, + private fb: UntypedFormBuilder) { + } + + ngOnInit(): void { + this.barSettingsFormGroup = this.fb.group({ + showBorder: [null, []], + borderWidth: [null, [Validators.min(0)]], + borderRadius: [null, [Validators.min(0)]], + showLabel: [null, []], + labelPosition: [null, []], + labelFont: [null, []], + labelColor: [null, []], + backgroundSettings: [null, []] + }); + this.barSettingsFormGroup.valueChanges.subscribe(() => { + this.updateModel(); + }); + merge(this.barSettingsFormGroup.get('showBorder').valueChanges, + this.barSettingsFormGroup.get('showLabel').valueChanges) + .subscribe(() => { + this.updateValidators(); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.barSettingsFormGroup.disable({emitEvent: false}); + } else { + this.barSettingsFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: BarSeriesSettings): void { + this.modelValue = value; + this.barSettingsFormGroup.patchValue( + value, {emitEvent: false} + ); + this.updateValidators(); + } + + private updateValidators() { + const showBorder: boolean = this.barSettingsFormGroup.get('showBorder').value; + const showLabel: boolean = this.barSettingsFormGroup.get('showLabel').value; + if (showBorder) { + this.barSettingsFormGroup.get('borderWidth').enable({emitEvent: false}); + } else { + this.barSettingsFormGroup.get('borderWidth').disable({emitEvent: false}); + } + if (showLabel) { + this.barSettingsFormGroup.get('labelPosition').enable({emitEvent: false}); + this.barSettingsFormGroup.get('labelFont').enable({emitEvent: false}); + this.barSettingsFormGroup.get('labelColor').enable({emitEvent: false}); + } else { + this.barSettingsFormGroup.get('labelPosition').disable({emitEvent: false}); + this.barSettingsFormGroup.get('labelFont').disable({emitEvent: false}); + this.barSettingsFormGroup.get('labelColor').disable({emitEvent: false}); + } + } + + private updateModel() { + this.modelValue = this.barSettingsFormGroup.getRawValue(); + this.propagateChange(this.modelValue); + } + + private _labelPreviewFn(): string { + const dataKey = this.dataKeyConfigComponent.modelValue; + const widgetConfig = this.dataKeyConfigComponent.widgetConfig; + const units = dataKey.units && dataKey.units.length ? dataKey.units : widgetConfig.config.units; + const decimals = isDefinedAndNotNull(dataKey.decimals) ? dataKey.decimals : + (isDefinedAndNotNull(widgetConfig.config.decimals) ? widgetConfig.config.decimals : 2); + return formatValue(22, decimals, units, false); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component.html new file mode 100644 index 0000000000..934f985483 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component.html @@ -0,0 +1,53 @@ + + +
+
+
{{ title | translate }}
+ + {{ seriesFillTypeTranslationMap.get(type) | translate }} + +
+ +
+
widgets.time-series-chart.series.opacity
+ + + +
+
+ +
+
widgets.time-series-chart.series.gradient-stops
+
+
widgets.time-series-chart.series.gradient-start
+ + + +
widgets.time-series-chart.series.gradient-end
+ + + +
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component.ts new file mode 100644 index 0000000000..44cc870f5b --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component.ts @@ -0,0 +1,143 @@ +/// +/// 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. +/// + +import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { + SeriesFillSettings, + SeriesFillType, + seriesFillTypes, + seriesFillTypeTranslations +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; + +@Component({ + selector: 'tb-time-series-chart-fill-settings', + templateUrl: './time-series-chart-fill-settings.component.html', + styleUrls: ['./../widget-settings.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TimeSeriesChartFillSettingsComponent), + multi: true + } + ] +}) +export class TimeSeriesChartFillSettingsComponent implements OnInit, ControlValueAccessor { + + seriesFillTypes = seriesFillTypes; + + seriesFillTypeTranslationMap: Map = new Map([]); + + SeriesFillType = SeriesFillType; + + @Input() + disabled: boolean; + + @Input() + title = 'widgets.time-series-chart.series.fill'; + + @Input() + fillNoneTitle = 'widgets.time-series-chart.series.fill-type-none'; + + private modelValue: SeriesFillSettings; + + private propagateChange = null; + + public fillSettingsFormGroup: UntypedFormGroup; + + constructor(protected store: Store, + private fb: UntypedFormBuilder) { + } + + ngOnInit(): void { + this.fillSettingsFormGroup = this.fb.group({ + type: [null, []], + opacity: [null, [Validators.min(0), Validators.max(100)]], + gradient: this.fb.group({ + start: [null, [Validators.min(0), Validators.max(100)]], + end: [null, [Validators.min(0), Validators.max(100)]] + }) + }); + this.fillSettingsFormGroup.valueChanges.subscribe(() => { + this.updateModel(); + }); + this.fillSettingsFormGroup.get('type').valueChanges.subscribe(() => { + this.updateValidators(); + }); + for (const type of seriesFillTypes) { + let translation: string; + if (type === SeriesFillType.none) { + translation = this.fillNoneTitle; + } else { + translation = seriesFillTypeTranslations.get(type); + } + this.seriesFillTypeTranslationMap.set(type, translation); + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.fillSettingsFormGroup.disable({emitEvent: false}); + } else { + this.fillSettingsFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: SeriesFillSettings): void { + this.modelValue = value; + this.fillSettingsFormGroup.patchValue( + value, {emitEvent: false} + ); + this.updateValidators(); + } + + private updateValidators() { + const type: SeriesFillType = this.fillSettingsFormGroup.get('type').value; + if (type === SeriesFillType.none) { + this.fillSettingsFormGroup.get('opacity').disable({emitEvent: false}); + this.fillSettingsFormGroup.get('gradient').disable({emitEvent: false}); + } else if (type === SeriesFillType.opacity) { + this.fillSettingsFormGroup.get('opacity').enable({emitEvent: false}); + this.fillSettingsFormGroup.get('gradient').disable({emitEvent: false}); + } else if (type === SeriesFillType.gradient) { + this.fillSettingsFormGroup.get('opacity').disable({emitEvent: false}); + this.fillSettingsFormGroup.get('gradient').enable({emitEvent: false}); + } + } + + private updateModel() { + const value: SeriesFillSettings = this.fillSettingsFormGroup.getRawValue(); + this.modelValue = value; + this.propagateChange(this.modelValue); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-key-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-key-settings.component.html new file mode 100644 index 0000000000..5f0fbb9e37 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-key-settings.component.html @@ -0,0 +1,62 @@ + + +
+
widgets.time-series-chart.series.legend-settings
+ +
+ {{ 'widgets.time-series-chart.series.show-in-legend' | translate }} +
+
+ +
+ {{ 'widgets.time-series-chart.series.hidden-by-default' | translate }} +
+
+
+
+
+
widgets.time-series-chart.axis.y-axis
+ + + + {{ yAxis }} + + + +
+
+
+
+
widgets.time-series-chart.series.series-type
+ + {{ timeSeriesChartSeriesTypeTranslations.get(type) | translate }} + +
+ + + + +
+
+ +
{{ timeSeriesChartTypeTranslations.get(chartType) | translate }}
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-key-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-key-settings.component.ts new file mode 100644 index 0000000000..d1ac428396 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-key-settings.component.ts @@ -0,0 +1,116 @@ +/// +/// 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. +/// + +import { Component } from '@angular/core'; +import { WidgetSettings, WidgetSettingsComponent } from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { isDefinedAndNotNull, mergeDeep } from '@core/utils'; +import { + timeSeriesChartKeyDefaultSettings, + TimeSeriesChartKeySettings, + TimeSeriesChartSeriesType, + timeSeriesChartSeriesTypes, + timeSeriesChartSeriesTypeTranslations, TimeSeriesChartType, timeSeriesChartTypeTranslations, TimeSeriesChartYAxisId +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; +import { TimeSeriesChartWidgetSettings } from '@home/components/widget/lib/chart/time-series-chart-widget.models'; + +@Component({ + selector: 'tb-time-series-chart-key-settings', + templateUrl: './time-series-chart-key-settings.component.html', + styleUrls: ['./../widget-settings.scss'] +}) +export class TimeSeriesChartKeySettingsComponent extends WidgetSettingsComponent { + + TimeSeriesChartType = TimeSeriesChartType; + + timeSeriesChartTypeTranslations = timeSeriesChartTypeTranslations; + + TimeSeriesChartSeriesType = TimeSeriesChartSeriesType; + + timeSeriesChartSeriesTypes = timeSeriesChartSeriesTypes; + + timeSeriesChartSeriesTypeTranslations = timeSeriesChartSeriesTypeTranslations; + + timeSeriesChartKeySettingsForm: UntypedFormGroup; + + chartType = TimeSeriesChartType.default; + + yAxisIds: TimeSeriesChartYAxisId[]; + + constructor(protected store: Store, + private fb: UntypedFormBuilder) { + super(store); + } + + protected settingsForm(): UntypedFormGroup { + return this.timeSeriesChartKeySettingsForm; + } + + protected onWidgetConfigSet(widgetConfig: WidgetConfigComponentData) { + const params = widgetConfig.typeParameters as any; + if (isDefinedAndNotNull(params.chartType)) { + this.chartType = params.chartType; + } + const widgetSettings = (widgetConfig.config?.settings || {}) as TimeSeriesChartWidgetSettings; + this.yAxisIds = widgetSettings.yAxes ? Object.keys(widgetSettings.yAxes) : ['default']; + } + + protected defaultSettings(): WidgetSettings { + return mergeDeep({} as TimeSeriesChartKeySettings, + timeSeriesChartKeyDefaultSettings); + } + + protected onSettingsSet(settings: WidgetSettings) { + const seriesSettings = settings as TimeSeriesChartKeySettings; + let yAxisId = seriesSettings.yAxisId; + if (!this.yAxisIds.includes(yAxisId)) { + yAxisId = 'default'; + } + this.timeSeriesChartKeySettingsForm = this.fb.group({ + yAxisId: [yAxisId, []], + showInLegend: [seriesSettings.showInLegend, []], + dataHiddenByDefault: [seriesSettings.dataHiddenByDefault, []], + type: [seriesSettings.type, []], + lineSettings: [seriesSettings.lineSettings, []], + barSettings: [seriesSettings.barSettings, []] + }); + } + + protected validatorTriggers(): string[] { + return ['showInLegend', 'type']; + } + + protected updateValidators(_emitEvent: boolean) { + const showInLegend: boolean = this.timeSeriesChartKeySettingsForm.get('showInLegend').value; + const type: TimeSeriesChartSeriesType = this.timeSeriesChartKeySettingsForm.get('type').value; + if (showInLegend) { + this.timeSeriesChartKeySettingsForm.get('dataHiddenByDefault').enable(); + } else { + this.timeSeriesChartKeySettingsForm.get('dataHiddenByDefault').patchValue(false, {emitEvent: false}); + this.timeSeriesChartKeySettingsForm.get('dataHiddenByDefault').disable(); + } + if (type === TimeSeriesChartSeriesType.line) { + this.timeSeriesChartKeySettingsForm.get('lineSettings').enable(); + this.timeSeriesChartKeySettingsForm.get('barSettings').disable(); + } else if (type === TimeSeriesChartSeriesType.bar) { + this.timeSeriesChartKeySettingsForm.get('lineSettings').disable(); + this.timeSeriesChartKeySettingsForm.get('barSettings').enable(); + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.html new file mode 100644 index 0000000000..78f1129727 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.html @@ -0,0 +1,116 @@ + + +
+
widgets.time-series-chart.series.line.line
+
+ + {{ 'widgets.time-series-chart.series.line.show-line' | translate }} + +
+
+ + {{ 'widgets.time-series-chart.series.line.step-line' | translate }} + + + + + {{ lineSeriesStepTypeTranslations.get(stepType) | translate }} + + + +
+
+ + {{ 'widgets.time-series-chart.series.line.smooth-line' | translate }} + +
+
+
widgets.time-series-chart.line-type
+ + + + {{ timeSeriesLineTypeTranslations.get(lineType) | translate }} + + + +
+
+
widgets.time-series-chart.line-width
+ + + +
+
+
+
widgets.time-series-chart.series.point.points
+
+ + {{ 'widgets.time-series-chart.series.point.show-points' | translate }} + +
+ +
+ + +
+ +
+ +
+ {{ 'widgets.time-series-chart.series.point.point-label' | translate }} +
+
+
+ + + + {{ seriesLabelPositionTranslations.get(position) | translate }} + + + + + + + +
+
+
+
widgets.time-series-chart.series.point.point-shape
+ + + + {{ timeSeriesChartShapeTranslations.get(shape) | translate }} + + + +
+
+
widgets.time-series-chart.series.point.point-size
+ + + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts new file mode 100644 index 0000000000..84533b26e4 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-line-settings.component.ts @@ -0,0 +1,192 @@ +/// +/// 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. +/// + +import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { + LineSeriesSettings, + lineSeriesStepTypes, + lineSeriesStepTypeTranslations, + seriesLabelPositions, + seriesLabelPositionTranslations, + timeSeriesChartShapes, + timeSeriesChartShapeTranslations, TimeSeriesChartType, + timeSeriesLineTypes, + timeSeriesLineTypeTranslations +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { merge } from 'rxjs'; +import { formatValue, isDefinedAndNotNull } from '@core/utils'; +import { DataKeyConfigComponent } from '@home/components/widget/config/data-key-config.component'; + +@Component({ + selector: 'tb-time-series-chart-line-settings', + templateUrl: './time-series-chart-line-settings.component.html', + styleUrls: ['./../widget-settings.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TimeSeriesChartLineSettingsComponent), + multi: true + } + ] +}) +export class TimeSeriesChartLineSettingsComponent implements OnInit, ControlValueAccessor { + + TimeSeriesChartType = TimeSeriesChartType; + + lineSeriesStepTypes = lineSeriesStepTypes; + + lineSeriesStepTypeTranslations = lineSeriesStepTypeTranslations; + + timeSeriesLineTypes = timeSeriesLineTypes; + + timeSeriesLineTypeTranslations = timeSeriesLineTypeTranslations; + + seriesLabelPositions = seriesLabelPositions; + + seriesLabelPositionTranslations = seriesLabelPositionTranslations; + + timeSeriesChartShapes = timeSeriesChartShapes; + + timeSeriesChartShapeTranslations = timeSeriesChartShapeTranslations; + + pointLabelPreviewFn = this._pointLabelPreviewFn.bind(this); + + @Input() + disabled: boolean; + + @Input() + chartType: TimeSeriesChartType; + + private modelValue: LineSeriesSettings; + + private propagateChange = null; + + public lineSettingsFormGroup: UntypedFormGroup; + + constructor(protected store: Store, + private dataKeyConfigComponent: DataKeyConfigComponent, + private fb: UntypedFormBuilder) { + } + + ngOnInit(): void { + this.lineSettingsFormGroup = this.fb.group({ + showLine: [null, []], + step: [null, []], + stepType: [null, []], + smooth: [null, []], + lineType: [null, []], + lineWidth: [null, [Validators.min(0)]], + showPoints: [null, []], + showPointLabel: [null, []], + pointLabelPosition: [null, []], + pointLabelFont: [null, []], + pointLabelColor: [null, []], + pointShape: [null, []], + pointSize: [null, [Validators.min(0)]], + fillAreaSettings: [null, []] + }); + this.lineSettingsFormGroup.valueChanges.subscribe(() => { + this.updateModel(); + }); + merge(this.lineSettingsFormGroup.get('showLine').valueChanges, + this.lineSettingsFormGroup.get('step').valueChanges, + this.lineSettingsFormGroup.get('showPointLabel').valueChanges) + .subscribe(() => { + this.updateValidators(); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.lineSettingsFormGroup.disable({emitEvent: false}); + } else { + this.lineSettingsFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: LineSeriesSettings): void { + this.modelValue = value; + this.lineSettingsFormGroup.patchValue( + value, {emitEvent: false} + ); + this.updateValidators(); + } + + private updateValidators() { + const showLine: boolean = this.lineSettingsFormGroup.get('showLine').value; + const step: boolean = this.lineSettingsFormGroup.get('step').value; + const showPointLabel: boolean = this.lineSettingsFormGroup.get('showPointLabel').value; + if (showLine) { + this.lineSettingsFormGroup.get('step').enable({emitEvent: false}); + if (step) { + this.lineSettingsFormGroup.get('stepType').enable({emitEvent: false}); + this.lineSettingsFormGroup.get('smooth').disable({emitEvent: false}); + } else { + this.lineSettingsFormGroup.get('stepType').disable({emitEvent: false}); + this.lineSettingsFormGroup.get('smooth').enable({emitEvent: false}); + } + this.lineSettingsFormGroup.get('lineType').enable({emitEvent: false}); + this.lineSettingsFormGroup.get('lineWidth').enable({emitEvent: false}); + } else { + this.lineSettingsFormGroup.get('step').disable({emitEvent: false}); + this.lineSettingsFormGroup.get('stepType').disable({emitEvent: false}); + this.lineSettingsFormGroup.get('smooth').disable({emitEvent: false}); + this.lineSettingsFormGroup.get('lineType').disable({emitEvent: false}); + this.lineSettingsFormGroup.get('lineWidth').disable({emitEvent: false}); + } + if (showPointLabel) { + this.lineSettingsFormGroup.get('pointLabelPosition').enable({emitEvent: false}); + this.lineSettingsFormGroup.get('pointLabelFont').enable({emitEvent: false}); + this.lineSettingsFormGroup.get('pointLabelColor').enable({emitEvent: false}); + } else { + this.lineSettingsFormGroup.get('pointLabelPosition').disable({emitEvent: false}); + this.lineSettingsFormGroup.get('pointLabelFont').disable({emitEvent: false}); + this.lineSettingsFormGroup.get('pointLabelColor').disable({emitEvent: false}); + } + } + + private updateModel() { + this.modelValue = this.lineSettingsFormGroup.getRawValue(); + this.propagateChange(this.modelValue); + } + + private _pointLabelPreviewFn(): string { + const dataKey = this.dataKeyConfigComponent.modelValue; + const widgetConfig = this.dataKeyConfigComponent.widgetConfig; + const units = dataKey.units && dataKey.units.length ? dataKey.units : widgetConfig.config.units; + const decimals = isDefinedAndNotNull(dataKey.decimals) ? dataKey.decimals : + (isDefinedAndNotNull(widgetConfig.config.decimals) ? widgetConfig.config.decimals : 2); + return formatValue(22, decimals, units, false); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html new file mode 100644 index 0000000000..cd6f1255f8 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.html @@ -0,0 +1,178 @@ + + + + + + +
+
widgets.time-series-chart.chart-style
+
+ + {{ 'widgets.time-series-chart.data-zoom' | translate }} + +
+
+ +
+ {{ 'widgets.time-series-chart.stack-mode' | translate }} +
+
+
+
+
widgets.time-series-chart.axis.x-axis
+ + +
+ + +
+ + + + + {{ 'widget-config.legend' | translate }} + + + + +
+
{{ 'legend.label' | translate }}
+
+ + + + +
+
+ + +
+
+
+
+ + + + + {{ 'widget-config.tooltip' | translate }} + + + + +
+
{{ 'tooltip.trigger' | translate }}
+ + {{ 'tooltip.trigger-point' | translate }} + {{ 'tooltip.trigger-axis' | translate }} + +
+
+
{{ 'tooltip.value' | translate }}
+
+ + + + +
+
+
+ + {{ 'tooltip.date' | translate }} + +
+ + + + + +
+
+
+ +
+ {{ 'tooltip.show-date-time-interval' | translate }} +
+
+
+
+
{{ 'tooltip.background-color' | translate }}
+ + +
+
+
{{ 'tooltip.background-blur' | translate }}
+ + +
px
+
+
+
+
+
+ + +
+
widget-config.card-appearance
+
+
{{ 'widgets.background.background' | translate }}
+ + +
+
+
{{ 'widget-config.card-padding' | translate }}
+ + + +
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.ts new file mode 100644 index 0000000000..6edab7ecf2 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component.ts @@ -0,0 +1,222 @@ +/// +/// 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. +/// + +import { Component, Injector } from '@angular/core'; +import { + DataKey, + Datasource, + legendPositions, + legendPositionTranslationMap, + WidgetSettings, + WidgetSettingsComponent +} from '@shared/models/widget.models'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { formatValue, isDefinedAndNotNull, mergeDeep } from '@core/utils'; +import { DateFormatProcessor, DateFormatSettings } from '@shared/models/widget-settings.models'; +import { EChartsTooltipTrigger } from '../../chart/echarts-widget.models'; +import { + timeSeriesChartWidgetDefaultSettings, + TimeSeriesChartWidgetSettings +} from '@home/components/widget/lib/chart/time-series-chart-widget.models'; +import { + TimeSeriesChartKeySettings, + TimeSeriesChartType, TimeSeriesChartYAxes, + TimeSeriesChartYAxisId +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { WidgetConfigComponentData } from '@home/models/widget-component.models'; + +@Component({ + selector: 'tb-time-series-chart-widget-settings', + templateUrl: './time-series-chart-widget-settings.component.html', + styleUrls: ['./../widget-settings.scss'] +}) +export class TimeSeriesChartWidgetSettingsComponent extends WidgetSettingsComponent { + + public get datasource(): Datasource { + const datasources: Datasource[] = this.widgetConfig.config.datasources; + if (datasources && datasources.length) { + return datasources[0]; + } else { + return null; + } + } + + public get yAxisIds(): TimeSeriesChartYAxisId[] { + const yAxes: TimeSeriesChartYAxes = this.timeSeriesChartWidgetSettingsForm.get('yAxes').value; + return Object.keys(yAxes); + } + + TimeSeriesChartType = TimeSeriesChartType; + + EChartsTooltipTrigger = EChartsTooltipTrigger; + + legendPositions = legendPositions; + + legendPositionTranslationMap = legendPositionTranslationMap; + + timeSeriesChartWidgetSettingsForm: UntypedFormGroup; + + tooltipValuePreviewFn = this._tooltipValuePreviewFn.bind(this); + + tooltipDatePreviewFn = this._tooltipDatePreviewFn.bind(this); + + chartType: TimeSeriesChartType = TimeSeriesChartType.default; + + constructor(protected store: Store, + private $injector: Injector, + private fb: UntypedFormBuilder) { + super(store); + } + + public yAxisRemoved(yAxisId: TimeSeriesChartYAxisId): void { + if (this.widgetConfig.config.datasources && this.widgetConfig.config.datasources.length > 1) { + for (let i = 1; i < this.widgetConfig.config.datasources.length; i++) { + const datasource = this.widgetConfig.config.datasources[i]; + this.removeYaxisId(datasource.dataKeys, yAxisId); + } + } + } + + protected settingsForm(): UntypedFormGroup { + return this.timeSeriesChartWidgetSettingsForm; + } + + protected onWidgetConfigSet(widgetConfig: WidgetConfigComponentData) { + const params = widgetConfig.typeParameters as any; + if (isDefinedAndNotNull(params.chartType)) { + this.chartType = params.chartType; + } + } + + protected defaultSettings(): WidgetSettings { + return mergeDeep({} as TimeSeriesChartWidgetSettings, timeSeriesChartWidgetDefaultSettings); + } + + protected onSettingsSet(settings: WidgetSettings) { + this.timeSeriesChartWidgetSettingsForm = this.fb.group({ + + yAxes: [settings.yAxes, []], + thresholds: [settings.thresholds, []], + + dataZoom: [settings.dataZoom, []], + stack: [settings.stack, []], + + xAxis: [settings.xAxis, []], + + noAggregationBarWidthSettings: [settings.noAggregationBarWidthSettings, []], + + showLegend: [settings.showLegend, []], + legendLabelFont: [settings.legendLabelFont, []], + legendLabelColor: [settings.legendLabelColor, []], + legendConfig: [settings.legendConfig, []], + + showTooltip: [settings.showTooltip, []], + tooltipTrigger: [settings.tooltipTrigger, []], + tooltipValueFont: [settings.tooltipValueFont, []], + tooltipValueColor: [settings.tooltipValueColor, []], + tooltipShowDate: [settings.tooltipShowDate, []], + tooltipDateFormat: [settings.tooltipDateFormat, []], + tooltipDateFont: [settings.tooltipDateFont, []], + tooltipDateColor: [settings.tooltipDateColor, []], + tooltipDateInterval: [settings.tooltipDateInterval, []], + + tooltipBackgroundColor: [settings.tooltipBackgroundColor, []], + tooltipBackgroundBlur: [settings.tooltipBackgroundBlur, []], + + animation: [settings.animation, []], + + background: [settings.background, []], + padding: [settings.padding, []] + }); + } + + protected validatorTriggers(): string[] { + return ['showLegend', 'showTooltip', 'tooltipShowDate']; + } + + protected updateValidators(emitEvent: boolean) { + const showLegend: boolean = this.timeSeriesChartWidgetSettingsForm.get('showLegend').value; + const showTooltip: boolean = this.timeSeriesChartWidgetSettingsForm.get('showTooltip').value; + const tooltipShowDate: boolean = this.timeSeriesChartWidgetSettingsForm.get('tooltipShowDate').value; + + if (showLegend) { + this.timeSeriesChartWidgetSettingsForm.get('legendLabelFont').enable(); + this.timeSeriesChartWidgetSettingsForm.get('legendLabelColor').enable(); + this.timeSeriesChartWidgetSettingsForm.get('legendConfig').enable(); + } else { + this.timeSeriesChartWidgetSettingsForm.get('legendLabelFont').disable(); + this.timeSeriesChartWidgetSettingsForm.get('legendLabelColor').disable(); + this.timeSeriesChartWidgetSettingsForm.get('legendConfig').disable(); + } + + if (showTooltip) { + this.timeSeriesChartWidgetSettingsForm.get('tooltipTrigger').enable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipValueFont').enable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipValueColor').enable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipShowDate').enable({emitEvent: false}); + this.timeSeriesChartWidgetSettingsForm.get('tooltipBackgroundColor').enable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipBackgroundBlur').enable(); + if (tooltipShowDate) { + this.timeSeriesChartWidgetSettingsForm.get('tooltipDateFormat').enable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipDateFont').enable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipDateColor').enable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipDateInterval').enable(); + } else { + this.timeSeriesChartWidgetSettingsForm.get('tooltipDateFormat').disable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipDateFont').disable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipDateColor').disable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipDateInterval').disable(); + } + } else { + this.timeSeriesChartWidgetSettingsForm.get('tooltipValueFont').disable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipValueColor').disable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipShowDate').disable({emitEvent: false}); + this.timeSeriesChartWidgetSettingsForm.get('tooltipDateFormat').disable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipDateFont').disable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipDateColor').disable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipDateInterval').disable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipBackgroundColor').disable(); + this.timeSeriesChartWidgetSettingsForm.get('tooltipBackgroundBlur').disable(); + } + } + + private removeYaxisId(series: DataKey[], yAxisId: TimeSeriesChartYAxisId): boolean { + let changed = false; + if (series) { + series.forEach(key => { + const keySettings = ((key.settings || {}) as TimeSeriesChartKeySettings); + if (keySettings.yAxisId === yAxisId) { + keySettings.yAxisId = 'default'; + changed = true; + } + }); + } + return changed; + } + + private _tooltipValuePreviewFn(): string { + return formatValue(22, 0, '°C', false); + } + + private _tooltipDatePreviewFn(): string { + const dateFormat: DateFormatSettings = this.timeSeriesChartWidgetSettingsForm.get('tooltipDateFormat').value; + const processor = DateFormatProcessor.fromSettings(this.$injector, dateFormat); + processor.update(Date.now()); + return processor.formatted; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.ts index b7183a5b80..6c515ad0a7 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/action/widget-action.component.ts @@ -28,6 +28,7 @@ import { Component, ElementRef, forwardRef, Input, OnInit, ViewChild } from '@an import { WidgetAction, WidgetActionType, + widgetActionTypes, widgetActionTypeTranslationMap, widgetType } from '@shared/models/widget.models'; @@ -88,7 +89,7 @@ export class WidgetActionComponent implements ControlValueAccessor, OnInit, Vali @Input() callbacks: WidgetActionCallbacks; - widgetActionTypes = Object.keys(WidgetActionType); + widgetActionTypes = widgetActionTypes; widgetActionTypeTranslations = widgetActionTypeTranslationMap; widgetActionType = WidgetActionType; diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-animation-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-animation-settings.component.html new file mode 100644 index 0000000000..5e0ad8e2ec --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-animation-settings.component.html @@ -0,0 +1,89 @@ + + +
+ + + + + {{ 'widgets.time-series-chart.animation.animation' | translate }} + + + + +
+
{{ 'widgets.time-series-chart.animation.animation-threshold' | translate }}
+ + +
ms
+
+
+
+
{{ 'widgets.time-series-chart.animation.animation-duration' | translate }}
+ + +
ms
+
+
+
+
widgets.time-series-chart.animation.animation-easing
+ + + + {{ easing }} + + + +
+
+
{{ 'widgets.time-series-chart.animation.animation-delay' | translate }}
+ + +
ms
+
+
+
+
{{ 'widgets.time-series-chart.animation.update-animation-duration' | translate }}
+ + +
ms
+
+
+
+
widgets.time-series-chart.animation.update-animation-easing
+ + + + {{ easing }} + + + +
+
+
{{ 'widgets.time-series-chart.animation.update-animation-delay' | translate }}
+ + +
ms
+
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-animation-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-animation-settings.component.ts new file mode 100644 index 0000000000..5da5aa2fe9 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-animation-settings.component.ts @@ -0,0 +1,124 @@ +/// +/// 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. +/// + +import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { + timeSeriesChartAnimationEasings, + TimeSeriesChartAnimationSettings +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { WidgetService } from '@core/http/widget.service'; + +@Component({ + selector: 'tb-time-series-chart-animation-settings', + templateUrl: './time-series-chart-animation-settings.component.html', + styleUrls: ['./../../widget-settings.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TimeSeriesChartAnimationSettingsComponent), + multi: true + } + ] +}) +export class TimeSeriesChartAnimationSettingsComponent implements OnInit, ControlValueAccessor { + + settingsExpanded = false; + + timeSeriesChartAnimationEasings = timeSeriesChartAnimationEasings; + + @Input() + disabled: boolean; + + private modelValue: TimeSeriesChartAnimationSettings; + + private propagateChange = null; + + public animationSettingsFormGroup: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder, + private widgetService: WidgetService,) { + } + + ngOnInit(): void { + this.animationSettingsFormGroup = this.fb.group({ + animation: [null, []], + animationThreshold: [null, [Validators.min(0)]], + animationDuration: [null, [Validators.min(0)]], + animationEasing: [null, []], + animationDelay: [null, [Validators.min(0)]], + animationDurationUpdate: [null, [Validators.min(0)]], + animationEasingUpdate: [null, []], + animationDelayUpdate: [null, [Validators.min(0)]] + }); + this.animationSettingsFormGroup.valueChanges.subscribe(() => { + this.updateModel(); + }); + this.animationSettingsFormGroup.get('animation').valueChanges + .subscribe(() => { + this.updateValidators(); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.animationSettingsFormGroup.disable({emitEvent: false}); + } else { + this.animationSettingsFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: TimeSeriesChartAnimationSettings): void { + this.modelValue = value; + this.animationSettingsFormGroup.patchValue( + value, {emitEvent: false} + ); + this.updateValidators(); + this.animationSettingsFormGroup.get('animation').valueChanges.subscribe((animation) => { + this.settingsExpanded = animation; + }); + } + + private updateValidators() { + const animation: boolean = this.animationSettingsFormGroup.get('animation').value; + if (animation) { + this.animationSettingsFormGroup.enable({emitEvent: false}); + } else { + this.animationSettingsFormGroup.disable({emitEvent: false}); + this.animationSettingsFormGroup.get('animation').enable({emitEvent: false}); + } + } + + private updateModel() { + this.modelValue = this.animationSettingsFormGroup.getRawValue(); + this.propagateChange(this.modelValue); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.html new file mode 100644 index 0000000000..9511baf121 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.html @@ -0,0 +1,149 @@ + + +
+ + + + + {{ 'widgets.time-series-chart.axis.show' | translate }} + + + + +
+
widgets.time-series-chart.axis.label
+
+ + + + + + + +
+
+
+
widgets.time-series-chart.axis.position
+ + + + {{ timeSeriesAxisPositionTranslations.get(position) | translate }} + + + +
+
+
widget-config.units-short
+ + +
+
+
widget-config.decimals-short
+ + + +
+
+
+ +
widgets.time-series-chart.axis.tick-labels
+
+
+ + + + +
+
+
+ + +
+
+
+ + {{ 'widgets.time-series-chart.axis.show-ticks' | translate }} + + + +
+
+ + {{ 'widgets.time-series-chart.axis.show-line' | translate }} + + + +
+
+ +
+ {{ 'widgets.time-series-chart.axis.show-split-lines' | translate }} +
+
+ + +
+
+
+
+
+
widgets.time-series-chart.axis.scale
+
+
widgets.time-series-chart.axis.scale-min
+ + + +
widgets.time-series-chart.axis.scale-max
+ + + +
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts new file mode 100644 index 0000000000..a88fc2e123 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component.ts @@ -0,0 +1,200 @@ +/// +/// 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. +/// + +import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { + AxisPosition, + timeSeriesAxisPositionTranslations, + TimeSeriesChartAxisSettings, + TimeSeriesChartYAxisSettings +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { merge } from 'rxjs'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { WidgetService } from '@core/http/widget.service'; + +@Component({ + selector: 'tb-time-series-chart-axis-settings', + templateUrl: './time-series-chart-axis-settings.component.html', + styleUrls: ['./../../widget-settings.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TimeSeriesChartAxisSettingsComponent), + multi: true + } + ] +}) +export class TimeSeriesChartAxisSettingsComponent implements OnInit, ControlValueAccessor { + + @Input() + @coerceBoolean() + alwaysExpanded = false; + + settingsExpanded = false; + + axisPositions: AxisPosition[]; + + timeSeriesAxisPositionTranslations = timeSeriesAxisPositionTranslations; + + functionScopeVariables = this.widgetService.getWidgetScopeVariables(); + + @Input() + disabled: boolean; + + @Input() + axisType: 'xAxis' | 'yAxis' = 'xAxis'; + + @Input() + @coerceBoolean() + advanced = false; + + private modelValue: TimeSeriesChartAxisSettings | TimeSeriesChartYAxisSettings; + + private propagateChange = null; + + public axisSettingsFormGroup: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder, + private widgetService: WidgetService,) { + } + + ngOnInit(): void { + + this.axisPositions = this.axisType === 'xAxis' ? [AxisPosition.top, AxisPosition.bottom] : + [AxisPosition.left, AxisPosition.right]; + + this.axisSettingsFormGroup = this.fb.group({ + show: [null, []], + label: [null, []], + labelFont: [null, []], + labelColor: [null, []], + position: [null, []], + showTickLabels: [null, []], + tickLabelFont: [null, []], + tickLabelColor: [null, []], + showTicks: [null, []], + ticksColor: [null, []], + showLine: [null, []], + lineColor: [null, []], + showSplitLines: [null, []], + splitLinesColor: [null, []] + }); + if (this.axisType === 'yAxis') { + this.axisSettingsFormGroup.addControl('units', this.fb.control(null, [])); + this.axisSettingsFormGroup.addControl('decimals', this.fb.control(null, [Validators.min(0)])); + this.axisSettingsFormGroup.addControl('ticksFormatter', this.fb.control(null, [])); + this.axisSettingsFormGroup.addControl('min', this.fb.control(null, [])); + this.axisSettingsFormGroup.addControl('max', this.fb.control(null, [])); + } + this.axisSettingsFormGroup.valueChanges.subscribe(() => { + this.updateModel(); + }); + merge(this.axisSettingsFormGroup.get('show').valueChanges, + this.axisSettingsFormGroup.get('showTickLabels').valueChanges, + this.axisSettingsFormGroup.get('showTicks').valueChanges, + this.axisSettingsFormGroup.get('showLine').valueChanges, + this.axisSettingsFormGroup.get('showSplitLines').valueChanges) + .subscribe(() => { + this.updateValidators(); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.axisSettingsFormGroup.disable({emitEvent: false}); + } else { + this.axisSettingsFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: TimeSeriesChartAxisSettings | TimeSeriesChartYAxisSettings): void { + this.modelValue = value; + this.axisSettingsFormGroup.patchValue( + value, {emitEvent: false} + ); + this.updateValidators(); + this.axisSettingsFormGroup.get('show').valueChanges.subscribe((show) => { + this.settingsExpanded = show; + }); + } + + private updateValidators() { + const show: boolean = this.axisSettingsFormGroup.get('show').value; + const showTickLabels: boolean = this.axisSettingsFormGroup.get('showTickLabels').value; + const showTicks: boolean = this.axisSettingsFormGroup.get('showTicks').value; + const showLine: boolean = this.axisSettingsFormGroup.get('showLine').value; + const showSplitLines: boolean = this.axisSettingsFormGroup.get('showSplitLines').value; + if (show) { + this.axisSettingsFormGroup.enable({emitEvent: false}); + if (showTickLabels) { + this.axisSettingsFormGroup.get('tickLabelFont').enable({emitEvent: false}); + this.axisSettingsFormGroup.get('tickLabelColor').enable({emitEvent: false}); + if (this.axisType === 'yAxis') { + this.axisSettingsFormGroup.get('ticksFormatter').enable({emitEvent: false}); + } + } else { + this.axisSettingsFormGroup.get('tickLabelFont').disable({emitEvent: false}); + this.axisSettingsFormGroup.get('tickLabelColor').disable({emitEvent: false}); + if (this.axisType === 'yAxis') { + this.axisSettingsFormGroup.get('ticksFormatter').disable({emitEvent: false}); + } + } + if (showTicks) { + this.axisSettingsFormGroup.get('ticksColor').enable({emitEvent: false}); + } else { + this.axisSettingsFormGroup.get('ticksColor').disable({emitEvent: false}); + } + if (showLine) { + this.axisSettingsFormGroup.get('lineColor').enable({emitEvent: false}); + } else { + this.axisSettingsFormGroup.get('lineColor').disable({emitEvent: false}); + } + if (showSplitLines) { + this.axisSettingsFormGroup.get('splitLinesColor').enable({emitEvent: false}); + } else { + this.axisSettingsFormGroup.get('splitLinesColor').disable({emitEvent: false}); + } + } else { + this.axisSettingsFormGroup.disable({emitEvent: false}); + this.axisSettingsFormGroup.get('show').enable({emitEvent: false}); + if (this.axisType === 'yAxis') { + this.axisSettingsFormGroup.get('min').enable({emitEvent: false}); + this.axisSettingsFormGroup.get('max').enable({emitEvent: false}); + } + } + } + + private updateModel() { + this.modelValue = this.axisSettingsFormGroup.getRawValue(); + this.propagateChange(this.modelValue); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html new file mode 100644 index 0000000000..7e2c9f74c2 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.html @@ -0,0 +1,117 @@ + +
+
+ + + + {{ timeSeriesThresholdTypeTranslations.get(type) | translate }} + + + + + +
+
+ + + + warning + + + + + + +
+
+ + + + {{ yAxis }} + + + +
+
+ + +
+
+ + +
+
+ + + +
+
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.scss new file mode 100644 index 0000000000..2ee42db765 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.scss @@ -0,0 +1,101 @@ +/** + * 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. + */ + +@import '../../../../../../../../../scss/constants'; + +.tb-form-table-row.tb-time-series-threshold-row { + @media #{$mat-lt-md} { + align-items: flex-start; + } + .tb-threshold-source-field { + flex: 1; + min-width: 100px; + display: flex; + flex-direction: column; + gap: 8px; + @media #{$mat-gt-sm} { + flex-direction: row; + align-items: center; + gap: 12px; + } + @media #{$mat-gt-md} { + min-width: 200px; + } + } + + .tb-threshold-type-field { + flex: 1; + } + + .tb-entity-alias-input { + flex: 1; + } + + .tb-threshold-key-value-field { + flex: 1; + min-width: 100px; + .tb-inline-field { + width: 100%; + } + @media #{$mat-gt-md} { + min-width: 150px; + } + } + + .tb-y-axis-field { + width: 100px; + min-width: 100px; + .mat-mdc-form-field { + width: 100%; + } + } + + .tb-color-field, .tb-units-field, .tb-decimals-field { + display: flex; + flex-direction: row; + place-content: center; + align-items: center; + } + + .tb-units-field { + width: 80px; + min-width: 80px; + } + + .tb-color-field { + width: 40px; + min-width: 40px; + } + + .tb-decimals-field { + width: 60px; + min-width: 60px; + } + + .tb-y-axis-field { + @media #{$mat-xs} { + display: none; + } + } + + .tb-units-field, .tb-color-field, .tb-decimals-field { + display: none; + @media #{$mat-gt-md} { + display: flex; + } + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts new file mode 100644 index 0000000000..baebf8c697 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component.ts @@ -0,0 +1,287 @@ +/// +/// 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. +/// + +import { + ChangeDetectorRef, + Component, + EventEmitter, + forwardRef, + Input, OnChanges, + OnInit, + Output, + Renderer2, SimpleChanges, + ViewContainerRef, + ViewEncapsulation +} from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { + TimeSeriesChartThreshold, + TimeSeriesChartThresholdType, TimeSeriesChartYAxisId, + timeSeriesThresholdTypes, + timeSeriesThresholdTypeTranslations +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { + TimeSeriesChartThresholdsPanelComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component'; +import { IAliasController } from '@core/api/widget-api.models'; +import { DataKey, Datasource, DatasourceType, WidgetConfig } from '@shared/models/widget.models'; +import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { deepClone } from '@core/utils'; +import { + TimeSeriesChartThresholdSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component'; + +@Component({ + selector: 'tb-time-series-chart-threshold-row', + templateUrl: './time-series-chart-threshold-row.component.html', + styleUrls: ['./time-series-chart-threshold-row.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TimeSeriesChartThresholdRowComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class TimeSeriesChartThresholdRowComponent implements ControlValueAccessor, OnInit, OnChanges { + + DataKeyType = DataKeyType; + + DatasourceType = DatasourceType; + + TimeSeriesChartThresholdType = TimeSeriesChartThresholdType; + + timeSeriesThresholdTypes = timeSeriesThresholdTypes; + + timeSeriesThresholdTypeTranslations = timeSeriesThresholdTypeTranslations; + + get aliasController(): IAliasController { + return this.thresholdsPanel.aliasController; + } + + get dataKeyCallbacks(): DataKeysCallbacks { + return this.thresholdsPanel.dataKeyCallbacks; + } + + get datasource(): Datasource { + return this.thresholdsPanel.datasource; + } + + get widgetConfig(): WidgetConfig { + return this.thresholdsPanel.widgetConfig; + } + + @Input() + disabled: boolean; + + @Input() + yAxisIds: TimeSeriesChartYAxisId[]; + + @Output() + thresholdRemoved = new EventEmitter(); + + thresholdFormGroup: UntypedFormGroup; + + modelValue: TimeSeriesChartThreshold; + + latestKeyFormControl: UntypedFormControl; + + entityKeyFormControl: UntypedFormControl; + + private propagateChange = (_val: any) => {}; + + constructor(private fb: UntypedFormBuilder, + private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, + private thresholdsPanel: TimeSeriesChartThresholdsPanelComponent, + private cd: ChangeDetectorRef) { + } + + ngOnInit() { + this.thresholdFormGroup = this.fb.group({ + type: [null, []], + value: [null, [Validators.required]], + entityAlias: [null, [Validators.required]], + yAxisId: [null, [Validators.required]], + lineColor: [null, []], + units: [null, []], + decimals: [null, []] + }); + this.latestKeyFormControl = this.fb.control(null, [Validators.required]); + this.entityKeyFormControl = this.fb.control(null, [Validators.required]); + this.thresholdFormGroup.valueChanges.subscribe( + () => this.updateModel() + ); + this.latestKeyFormControl.valueChanges.subscribe( + () => this.updateModel() + ); + this.entityKeyFormControl.valueChanges.subscribe( + () => this.updateModel() + ); + this.thresholdFormGroup.get('type').valueChanges.subscribe(() => { + this.updateValidators(); + }); + } + + ngOnChanges(changes: SimpleChanges): void { + for (const propName of Object.keys(changes)) { + const change = changes[propName]; + if (!change.firstChange && change.currentValue !== change.previousValue) { + if (['yAxisIds'].includes(propName)) { + if (this.modelValue?.yAxisId && + !this.yAxisIds.includes(this.modelValue.yAxisId)) { + this.thresholdFormGroup.patchValue({yAxisId: 'default'}, {emitEvent: true}); + } + } + } + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.thresholdFormGroup.disable({emitEvent: false}); + this.latestKeyFormControl.disable({emitEvent: false}); + this.entityKeyFormControl.disable({emitEvent: false}); + } else { + this.thresholdFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: TimeSeriesChartThreshold): void { + this.modelValue = value; + this.thresholdFormGroup.patchValue( + { + type: value.type, + value: value.value, + entityAlias: value.entityAlias, + yAxisId: value.yAxisId, + lineColor: value.lineColor, + units: value.units, + decimals: value.decimals, + }, {emitEvent: false} + ); + if (value.type === TimeSeriesChartThresholdType.latestKey) { + this.latestKeyFormControl.patchValue({ + type: value.latestKeyType, + name: value.latestKey + }, {emitEvent: false}); + } else if (value.type === TimeSeriesChartThresholdType.entity) { + this.entityKeyFormControl.patchValue({ + type: value.entityKeyType, + name: value.entityKey + }, {emitEvent: false}); + } + this.updateValidators(); + this.cd.markForCheck(); + } + + editThreshold($event: Event, matButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const ctx: any = { + thresholdSettings: deepClone(this.modelValue), + widgetConfig: this.widgetConfig, + yAxisIds: this.yAxisIds + }; + const thresholdSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, TimeSeriesChartThresholdSettingsPanelComponent, ['leftOnly', 'leftTopOnly', 'leftBottomOnly'], true, null, + ctx, + {}, + {}, {}, true); + thresholdSettingsPanelPopover.tbComponentRef.instance.popover = thresholdSettingsPanelPopover; + thresholdSettingsPanelPopover.tbComponentRef.instance.thresholdSettingsApplied.subscribe((thresholdSettings) => { + thresholdSettingsPanelPopover.hide(); + this.modelValue = {...this.modelValue, ...thresholdSettings}; + this.thresholdFormGroup.patchValue( + { + yAxisId: this.modelValue.yAxisId, + units: this.modelValue.units, + decimals: this.modelValue.decimals, + lineColor: this.modelValue.lineColor + }, + {emitEvent: false}); + this.propagateChange(this.modelValue); + }); + } + } + + private updateValidators() { + const type: TimeSeriesChartThresholdType = this.thresholdFormGroup.get('type').value; + if (type === TimeSeriesChartThresholdType.constant) { + this.thresholdFormGroup.get('value').enable({emitEvent: false}); + this.thresholdFormGroup.get('entityAlias').disable({emitEvent: false}); + this.latestKeyFormControl.disable({emitEvent: false}); + this.entityKeyFormControl.disable({emitEvent: false}); + } else if (type === TimeSeriesChartThresholdType.latestKey) { + this.thresholdFormGroup.get('value').disable({emitEvent: false}); + this.thresholdFormGroup.get('entityAlias').disable({emitEvent: false}); + this.latestKeyFormControl.enable({emitEvent: false}); + this.entityKeyFormControl.disable({emitEvent: false}); + } else if (type === TimeSeriesChartThresholdType.entity) { + this.thresholdFormGroup.get('value').disable({emitEvent: false}); + this.thresholdFormGroup.get('entityAlias').enable({emitEvent: false}); + this.latestKeyFormControl.disable({emitEvent: false}); + this.entityKeyFormControl.enable({emitEvent: false}); + } + } + + private updateModel() { + const value = this.thresholdFormGroup.value; + this.modelValue.type = value.type; + this.modelValue.value = value.value; + this.modelValue.entityAlias = value.entityAlias; + this.modelValue.yAxisId = value.yAxisId; + this.modelValue.lineColor = value.lineColor; + this.modelValue.units = value.units; + this.modelValue.decimals = value.decimals; + if (value.type === TimeSeriesChartThresholdType.latestKey) { + const latestKey: DataKey = this.latestKeyFormControl.value; + this.modelValue.latestKey = latestKey?.name; + this.modelValue.latestKeyType = (latestKey?.type as any); + } else if (value.type === TimeSeriesChartThresholdType.entity) { + const entityKey: DataKey = this.entityKeyFormControl.value; + this.modelValue.entityKey = entityKey?.name; + this.modelValue.entityKeyType = (entityKey?.type as any); + } + this.propagateChange(this.modelValue); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.html new file mode 100644 index 0000000000..3437a578d6 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.html @@ -0,0 +1,143 @@ + +
+
{{ 'widgets.time-series-chart.threshold.threshold-settings' | translate }}
+
+
+
widgets.time-series-chart.axis.y-axis
+ + + + {{ yAxis }} + + + +
+
+
widget-config.units-short
+ + +
+
+
widget-config.decimals-short
+ + + +
+
+ + {{ 'widgets.time-series-chart.threshold.label' | translate }} + +
+ + + + {{ timeSeriesThresholdLabelPositionTranslations.get(position) | translate }} + + + + + + + +
+
+
+
widgets.time-series-chart.threshold.line-appearance
+
+
widgets.time-series-chart.threshold.line-color
+ + +
+
+
widgets.time-series-chart.line-type
+ + + + {{ timeSeriesLineTypeTranslations.get(lineType) | translate }} + + + +
+
+
widgets.time-series-chart.line-width
+ + + +
+
+
widgets.time-series-chart.threshold.start-symbol
+
+ + + + {{ timeSeriesChartShapeTranslations.get(shape) | translate }} + + + +
widgets.time-series-chart.threshold.symbol-size
+ + + +
+
+
+
widgets.time-series-chart.threshold.end-symbol
+
+ + + + {{ timeSeriesChartShapeTranslations.get(shape) | translate }} + + + +
widgets.time-series-chart.threshold.symbol-size
+ + + +
+
+
+
+
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.scss new file mode 100644 index 0000000000..b65fa6ad65 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.scss @@ -0,0 +1,49 @@ +/** + * 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. + */ +@import '../../../../../../../../../scss/constants'; + +.tb-threshold-settings-panel { + width: 530px; + display: flex; + flex-direction: column; + gap: 16px; + @media #{$mat-lt-md} { + width: 90vw; + } + .tb-threshold-settings-panel-content { + display: flex; + flex-direction: column; + gap: 16px; + overflow: auto; + margin: -10px; + padding: 10px; + } + .tb-threshold-settings-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + .tb-threshold-settings-panel-buttons { + height: 40px; + display: flex; + flex-direction: row; + gap: 16px; + justify-content: flex-end; + align-items: flex-end; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.ts new file mode 100644 index 0000000000..b9f16103bd --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component.ts @@ -0,0 +1,146 @@ +/// +/// 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. +/// + +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; +import { + TimeSeriesChartShape, + timeSeriesChartShapes, + timeSeriesChartShapeTranslations, + TimeSeriesChartThreshold, TimeSeriesChartYAxisId, + timeSeriesLineTypes, + timeSeriesLineTypeTranslations, + timeSeriesThresholdLabelPositions, + timeSeriesThresholdLabelPositionTranslations +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { merge } from 'rxjs'; +import { WidgetConfig } from '@shared/models/widget.models'; +import { formatValue, isDefinedAndNotNull } from '@core/utils'; + +@Component({ + selector: 'tb-time-series-chart-threshold-settings-panel', + templateUrl: './time-series-chart-threshold-settings-panel.component.html', + providers: [], + styleUrls: ['./time-series-chart-threshold-settings-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class TimeSeriesChartThresholdSettingsPanelComponent implements OnInit { + + timeSeriesLineTypes = timeSeriesLineTypes; + + timeSeriesLineTypeTranslations = timeSeriesLineTypeTranslations; + + timeSeriesChartShapes = timeSeriesChartShapes; + + timeSeriesChartShapeTranslations = timeSeriesChartShapeTranslations; + + timeSeriesThresholdLabelPositions = timeSeriesThresholdLabelPositions; + + timeSeriesThresholdLabelPositionTranslations = timeSeriesThresholdLabelPositionTranslations; + + labelPreviewFn = this._labelPreviewFn.bind(this); + + @Input() + thresholdSettings: Partial; + + @Input() + widgetConfig: WidgetConfig; + + @Input() + yAxisIds: TimeSeriesChartYAxisId[]; + + @Input() + popover: TbPopoverComponent; + + @Output() + thresholdSettingsApplied = new EventEmitter>(); + + thresholdSettingsFormGroup: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + } + + ngOnInit(): void { + this.thresholdSettingsFormGroup = this.fb.group( + { + yAxisId: [this.thresholdSettings.yAxisId, [Validators.required]], + units: [this.thresholdSettings.units, []], + decimals: [this.thresholdSettings.decimals, [Validators.min(0)]], + lineColor: [this.thresholdSettings.lineColor, []], + lineType: [this.thresholdSettings.lineType, []], + lineWidth: [this.thresholdSettings.lineWidth, [Validators.min(0)]], + startSymbol: [this.thresholdSettings.startSymbol, []], + startSymbolSize: [this.thresholdSettings.startSymbolSize, [Validators.min(0)]], + endSymbol: [this.thresholdSettings.endSymbol, []], + endSymbolSize: [this.thresholdSettings.endSymbolSize, [Validators.min(0)]], + showLabel: [this.thresholdSettings.showLabel, []], + labelPosition: [this.thresholdSettings.labelPosition, []], + labelFont: [this.thresholdSettings.labelFont, []], + labelColor: [this.thresholdSettings.labelColor, []] + } + ); + merge(this.thresholdSettingsFormGroup.get('showLabel').valueChanges, + this.thresholdSettingsFormGroup.get('startSymbol').valueChanges, + this.thresholdSettingsFormGroup.get('endSymbol').valueChanges).subscribe(() => { + this.updateValidators(); + }); + this.updateValidators(); + } + + cancel() { + this.popover?.hide(); + } + + applyThresholdSettings() { + const thresholdSettings = this.thresholdSettingsFormGroup.getRawValue(); + this.thresholdSettingsApplied.emit(thresholdSettings); + } + + private updateValidators() { + const showLabel: boolean = this.thresholdSettingsFormGroup.get('showLabel').value; + const startSymbol: TimeSeriesChartShape = this.thresholdSettingsFormGroup.get('startSymbol').value; + const endSymbol: TimeSeriesChartShape = this.thresholdSettingsFormGroup.get('endSymbol').value; + if (showLabel) { + this.thresholdSettingsFormGroup.get('labelPosition').enable({emitEvent: false}); + this.thresholdSettingsFormGroup.get('labelFont').enable({emitEvent: false}); + this.thresholdSettingsFormGroup.get('labelColor').enable({emitEvent: false}); + } else { + this.thresholdSettingsFormGroup.get('labelPosition').disable({emitEvent: false}); + this.thresholdSettingsFormGroup.get('labelFont').disable({emitEvent: false}); + this.thresholdSettingsFormGroup.get('labelColor').disable({emitEvent: false}); + } + if (startSymbol === TimeSeriesChartShape.none) { + this.thresholdSettingsFormGroup.get('startSymbolSize').disable({emitEvent: false}); + } else { + this.thresholdSettingsFormGroup.get('startSymbolSize').enable({emitEvent: false}); + } + if (endSymbol === TimeSeriesChartShape.none) { + this.thresholdSettingsFormGroup.get('endSymbolSize').disable({emitEvent: false}); + } else { + this.thresholdSettingsFormGroup.get('endSymbolSize').enable({emitEvent: false}); + } + } + + private _labelPreviewFn(): string { + let units: string = this.thresholdSettingsFormGroup.get('units').value; + units = units && units.length ? units : this.widgetConfig.units; + let decimals: number = this.thresholdSettingsFormGroup.get('decimals').value; + decimals = isDefinedAndNotNull(decimals) ? decimals : + (isDefinedAndNotNull(this.widgetConfig.decimals) ? this.widgetConfig.decimals : 2); + return formatValue(22, decimals, units, false); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html new file mode 100644 index 0000000000..e975eb4653 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.html @@ -0,0 +1,50 @@ + +
+
{{ 'widgets.time-series-chart.threshold.thresholds' | translate }}
+
+
+
widgets.time-series-chart.threshold.source
+
widgets.time-series-chart.threshold.key-value
+
widgets.time-series-chart.axis.y-axis
+
widgets.color.color
+
widget-config.units-short
+
widget-config.decimals-short
+
+
+
+
+ + + +
+
+
+
+ +
+
+ + {{ 'widgets.time-series-chart.threshold.no-thresholds' | translate }} + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.scss new file mode 100644 index 0000000000..e59fb35a72 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.scss @@ -0,0 +1,85 @@ +/** + * 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. + */ + +@import '../../../../../../../../../scss/constants'; + +.tb-time-series-thresholds-panel { + .tb-form-table-header-cell { + &.tb-threshold-source-header { + flex: 1; + min-width: 100px; + @media #{$mat-gt-md} { + min-width: 200px; + } + } + &.tb-threshold-key-value-header { + flex: 1; + min-width: 100px; + @media #{$mat-gt-md} { + min-width: 150px; + } + } + + &.tb-y-axis-header { + width: 100px; + min-width: 100px; + } + + &.tb-units-header { + width: 80px; + min-width: 80px; + } + + &.tb-color-header { + width: 40px; + min-width: 40px; + } + + &.tb-decimals-header { + width: 60px; + min-width: 60px; + } + + &.tb-actions-header { + width: 80px; + min-width: 80px; + } + + &.tb-y-axis-header { + @media #{$mat-xs} { + display: none; + } + } + + &.tb-units-header, &.tb-color-header, &.tb-decimals-header { + display: none; + @media #{$mat-gt-md} { + display: block; + } + } + } + .tb-form-table-body { + .tb-time-series-threshold-row { + overflow: hidden; + } + .mat-divider { + margin-top: 8px; + @media #{$mat-gt-sm} { + display: none; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts new file mode 100644 index 0000000000..9cb5e5ced2 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component.ts @@ -0,0 +1,216 @@ +/// +/// 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. +/// + +import { Component, forwardRef, Input, OnInit, ViewEncapsulation } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormArray, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validator +} from '@angular/forms'; +import { + TimeSeriesChartThreshold, + timeSeriesChartThresholdDefaultSettings, + TimeSeriesChartThresholdType, + timeSeriesChartThresholdValid, + timeSeriesChartThresholdValidator, TimeSeriesChartYAxisId +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { mergeDeep } from '@core/utils'; +import { IAliasController } from '@core/api/widget-api.models'; +import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { DataKey, Datasource, WidgetConfig } from '@shared/models/widget.models'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; + +@Component({ + selector: 'tb-time-series-chart-thresholds-panel', + templateUrl: './time-series-chart-thresholds-panel.component.html', + styleUrls: ['./time-series-chart-thresholds-panel.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TimeSeriesChartThresholdsPanelComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => TimeSeriesChartThresholdsPanelComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class TimeSeriesChartThresholdsPanelComponent implements ControlValueAccessor, OnInit, Validator { + + @Input() + disabled: boolean; + + @Input() + aliasController: IAliasController; + + @Input() + dataKeyCallbacks: DataKeysCallbacks; + + @Input() + datasource: Datasource; + + @Input() + widgetConfig: WidgetConfig; + + @Input() + yAxisIds: TimeSeriesChartYAxisId[]; + + thresholdsFormGroup: UntypedFormGroup; + + private propagateChange = (_val: any) => {}; + + constructor(private fb: UntypedFormBuilder) { + } + + ngOnInit() { + this.thresholdsFormGroup = this.fb.group({ + thresholds: [this.fb.array([]), []] + }); + this.thresholdsFormGroup.valueChanges.subscribe( + () => { + let thresholds: TimeSeriesChartThreshold[] = this.thresholdsFormGroup.get('thresholds').value; + if (thresholds) { + thresholds = thresholds.filter(t => timeSeriesChartThresholdValid(t)); + } + this.updateLatestDataKeys(thresholds); + this.propagateChange(thresholds); + } + ); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.thresholdsFormGroup.disable({emitEvent: false}); + } else { + this.thresholdsFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: TimeSeriesChartThreshold[] | undefined): void { + const thresholds = this.checkLatestDataKeys(value || []); + this.thresholdsFormGroup.setControl('thresholds', this.prepareThresholdsFormArray(thresholds), {emitEvent: false}); + } + + public validate(c: UntypedFormControl) { + const valid = this.thresholdsFormGroup.valid; + return valid ? null : { + thresholds: { + valid: false, + }, + }; + } + + thresholdsFormArray(): UntypedFormArray { + return this.thresholdsFormGroup.get('thresholds') as UntypedFormArray; + } + + trackByThreshold(index: number, thresholdControl: AbstractControl): any { + return thresholdControl; + } + + removeThreshold(index: number) { + (this.thresholdsFormGroup.get('thresholds') as UntypedFormArray).removeAt(index); + } + + addThreshold() { + const threshold = mergeDeep({} as TimeSeriesChartThreshold, + timeSeriesChartThresholdDefaultSettings); + const thresholdsArray = this.thresholdsFormGroup.get('thresholds') as UntypedFormArray; + const thresholdControl = this.fb.control(threshold, [timeSeriesChartThresholdValidator]); + thresholdsArray.push(thresholdControl); + } + + private prepareThresholdsFormArray(thresholds: TimeSeriesChartThreshold[] | undefined): UntypedFormArray { + const thresholdsControls: Array = []; + if (thresholds) { + thresholds.forEach((threshold) => { + thresholdsControls.push(this.fb.control(threshold, [timeSeriesChartThresholdValidator])); + }); + } + return this.fb.array(thresholdsControls); + } + + private checkLatestDataKeys(thresholds: TimeSeriesChartThreshold[]): TimeSeriesChartThreshold[] { + const result: TimeSeriesChartThreshold[] = []; + const latestKeys = this.datasource?.latestDataKeys || []; + for (const threshold of thresholds) { + if (threshold.type === TimeSeriesChartThresholdType.latestKey) { + const found = latestKeys.find(k => this.isThresholdKey(k, threshold)); + if (found) { + result.push(threshold); + } + } else { + result.push(threshold); + } + } + return result; + } + + private updateLatestDataKeys(thresholds: TimeSeriesChartThreshold[]) { + if (this.datasource) { + let latestKeys = this.datasource.latestDataKeys; + if (!latestKeys) { + latestKeys = []; + this.datasource.latestDataKeys = latestKeys; + } + const existingThresholdKeys = latestKeys.filter(k => k.settings?.__thresholdKey === true); + const foundThresholdKeys: DataKey[] = []; + for (const threshold of thresholds) { + if (threshold.type === TimeSeriesChartThresholdType.latestKey) { + const found = existingThresholdKeys.find(k => this.isThresholdKey(k, threshold)); + if (!found) { + const newKey = this.dataKeyCallbacks.generateDataKey(threshold.latestKey, threshold.latestKeyType, + null, true, null); + newKey.settings.__thresholdKey = true; + latestKeys.push(newKey); + } else if (foundThresholdKeys.indexOf(found) === -1) { + foundThresholdKeys.push(found); + } + } + } + const toRemove = existingThresholdKeys.filter(k => foundThresholdKeys.indexOf(k) === -1); + for (const key of toRemove) { + const index = latestKeys.indexOf(key); + if (index > -1) { + latestKeys.splice(index, 1); + } + } + } + } + + private isThresholdKey(d: DataKey, threshold: TimeSeriesChartThreshold): boolean { + return (d.type === DataKeyType.function && d.label === threshold.latestKey) || + (d.type !== DataKeyType.function && d.name === threshold.latestKey && + d.type === threshold.latestKeyType); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.html new file mode 100644 index 0000000000..d018b638d0 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.html @@ -0,0 +1,63 @@ + +
+
{{ 'widgets.time-series-chart.y-axes' | translate }}
+
+
+
widgets.time-series-chart.axis.id
+
widgets.time-series-chart.axis.show
+
widgets.time-series-chart.axis.label
+
widgets.time-series-chart.axis.position
+
widgets.time-series-chart.axis.min
+
widgets.time-series-chart.axis.max
+
widget-config.units-short
+
widget-config.decimals-short
+
+
+
+
+ + +
+ +
+
+
+
+
+ +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.scss new file mode 100644 index 0000000000..52aa69f59d --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.scss @@ -0,0 +1,87 @@ +/** + * 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. + */ +@import '../../../../../../../../../scss/constants'; + +.tb-y-axes-panel { + .tb-form-table-header-cell { + &.tb-axis-id-header { + width: 60px; + min-width: 60px; + } + + &.tb-label-header { + flex: 1 1 60%; + min-width: 100px; + } + + &.tb-position-header { + flex: 1; + min-width: 60px; + @media #{$mat-gt-md} { + flex: 1 1 40%; + } + } + + &.tb-units-header { + width: 80px; + min-width: 80px; + } + + &.tb-decimals-header { + width: 60px; + min-width: 60px; + } + + &.tb-min-header, &.tb-max-header { + width: 80px; + min-width: 80px; + } + + &.tb-show-header { + width: 40px; + min-width: 40px; + } + + &.tb-actions-header { + width: 80px; + min-width: 80px; + @media #{$mat-gt-md} { + width: 120px; + min-width: 120px; + } + } + + &.tb-label-header, &.tb-units-header, &.tb-decimals-header { + display: none; + @media #{$mat-gt-md} { + display: block; + } + } + + &.tb-min-header, &.tb-max-header { + display: none; + @media #{$mat-gt-xs} { + display: block; + } + } + } + + .tb-form-table-body { + tb-time-series-chart-y-axis-row { + overflow: hidden; + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts new file mode 100644 index 0000000000..9879ffe877 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component.ts @@ -0,0 +1,180 @@ +/// +/// 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. +/// + +import { Component, EventEmitter, forwardRef, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { + AbstractControl, + ControlValueAccessor, + NG_VALIDATORS, + NG_VALUE_ACCESSOR, + UntypedFormArray, + UntypedFormBuilder, + UntypedFormControl, + UntypedFormGroup, + Validator +} from '@angular/forms'; +import { + defaultTimeSeriesChartYAxisSettings, + getNextTimeSeriesYAxisId, + TimeSeriesChartYAxes, TimeSeriesChartYAxisId, + TimeSeriesChartYAxisSettings, + timeSeriesChartYAxisValid, + timeSeriesChartYAxisValidator +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { mergeDeep } from '@core/utils'; +import { CdkDragDrop } from '@angular/cdk/drag-drop'; +import { coerceBoolean } from '@shared/decorators/coercion'; + +@Component({ + selector: 'tb-time-series-chart-y-axes-panel', + templateUrl: './time-series-chart-y-axes-panel.component.html', + styleUrls: ['./time-series-chart-y-axes-panel.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TimeSeriesChartYAxesPanelComponent), + multi: true + }, + { + provide: NG_VALIDATORS, + useExisting: forwardRef(() => TimeSeriesChartYAxesPanelComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class TimeSeriesChartYAxesPanelComponent implements ControlValueAccessor, OnInit, Validator { + + @Input() + disabled: boolean; + + @Input() + @coerceBoolean() + advanced = false; + + @Output() + axisRemoved = new EventEmitter(); + + yAxesFormGroup: UntypedFormGroup; + + get dragEnabled(): boolean { + return this.axesFormArray().controls.length > 1; + } + + private propagateChange = (_val: any) => {}; + + constructor(private fb: UntypedFormBuilder) { + } + + ngOnInit() { + this.yAxesFormGroup = this.fb.group({ + axes: [this.fb.array([]), []] + }); + this.yAxesFormGroup.valueChanges.subscribe( + () => { + let axes: TimeSeriesChartYAxisSettings[] = this.yAxesFormGroup.get('axes').value; + for (let i = 0; i < axes.length; i++) { + axes[i].order = i; + } + if (axes) { + axes = axes.filter(axis => timeSeriesChartYAxisValid(axis)); + } + const yAxes: TimeSeriesChartYAxes = {}; + for (const axis of axes) { + yAxes[axis.id] = axis; + } + this.propagateChange(yAxes); + } + ); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.yAxesFormGroup.disable({emitEvent: false}); + } else { + this.yAxesFormGroup.enable({emitEvent: false}); + } + } + + writeValue(value: TimeSeriesChartYAxes | undefined): void { + const yAxes: TimeSeriesChartYAxes = value || {}; + if (!yAxes.default) { + yAxes.default = mergeDeep({} as TimeSeriesChartYAxisSettings, defaultTimeSeriesChartYAxisSettings, + {id: 'default', order: 0} as TimeSeriesChartYAxisSettings); + } + const yAxisSettingsList = Object.values(yAxes); + yAxisSettingsList.sort((a1, a2) => a1.order - a2.order); + this.yAxesFormGroup.setControl('axes', this.prepareAxesFormArray(yAxisSettingsList), {emitEvent: false}); + } + + public validate(c: UntypedFormControl) { + const valid = this.yAxesFormGroup.valid; + return valid ? null : { + yAxes: { + valid: false, + }, + }; + } + + axisDrop(event: CdkDragDrop) { + const axesArray = this.yAxesFormGroup.get('axes') as UntypedFormArray; + const axis = axesArray.at(event.previousIndex); + axesArray.removeAt(event.previousIndex); + axesArray.insert(event.currentIndex, axis); + } + + axesFormArray(): UntypedFormArray { + return this.yAxesFormGroup.get('axes') as UntypedFormArray; + } + + trackByAxis(index: number, axisControl: AbstractControl): any { + return axisControl; + } + + removeAxis(index: number) { + const axis = + (this.yAxesFormGroup.get('axes') as UntypedFormArray).at(index).value as TimeSeriesChartYAxisSettings; + (this.yAxesFormGroup.get('axes') as UntypedFormArray).removeAt(index); + this.axisRemoved.emit(axis.id); + } + + addAxis() { + const axis = mergeDeep({} as TimeSeriesChartYAxisSettings, + defaultTimeSeriesChartYAxisSettings); + const axes: TimeSeriesChartYAxisSettings[] = this.yAxesFormGroup.get('axes').value; + axis.id = getNextTimeSeriesYAxisId(axes); + axis.order = axes.length; + const axesArray = this.yAxesFormGroup.get('axes') as UntypedFormArray; + const axisControl = this.fb.control(axis, [timeSeriesChartYAxisValidator]); + axesArray.push(axisControl); + } + + private prepareAxesFormArray(axes: TimeSeriesChartYAxisSettings[]): UntypedFormArray { + const axesControls: Array = []; + axes.forEach((axis) => { + axesControls.push(this.fb.control(axis, [timeSeriesChartYAxisValidator])); + }); + return this.fb.array(axesControls); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.html new file mode 100644 index 0000000000..0b1907033f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.html @@ -0,0 +1,72 @@ + +
+
+ {{ modelValue.id }} +
+ + + + + + + + {{ timeSeriesAxisPositionTranslations.get(position) | translate }} + + + +
+ + + +
+
+ + + +
+
+ + +
+
+ + + +
+
+ +
+ +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.scss new file mode 100644 index 0000000000..12db0d9228 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.scss @@ -0,0 +1,95 @@ +/** + * 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. + */ +@import '../../../../../../../../../scss/constants'; + +.tb-form-table-row.tb-axis-row { + + .tb-axis-id-field { + color: rgba(0, 0, 0, 0.76); + font-weight: 500; + font-size: 14px; + line-height: 20px; + letter-spacing: 0.2px; + width: 60px; + min-width: 60px; + } + + .tb-label-field { + flex: 1 1 60%; + min-width: 100px; + } + + .tb-position-field { + flex: 1; + min-width: 60px; + @media #{$mat-gt-md} { + flex: 1 1 40%; + } + } + + .tb-units-field, .tb-decimals-field, .tb-min-field, .tb-max-field { + display: flex; + flex-direction: row; + place-content: center; + align-items: center; + } + + .tb-units-field { + width: 80px; + min-width: 80px; + } + + .tb-decimals-field { + width: 60px; + min-width: 60px; + } + + .tb-min-field, .tb-max-field { + width: 80px; + min-width: 80px; + } + + .tb-show-field { + width: 40px; + min-width: 40px; + } + + .tb-label-field { + display: none; + @media #{$mat-gt-md} { + display: block; + } + } + + .tb-units-field, .tb-decimals-field { + display: none; + @media #{$mat-gt-md} { + display: flex; + } + } + + .tb-min-field, .tb-max-field { + display: none; + @media #{$mat-gt-xs} { + display: flex; + } + } + + .tb-remove-button { + width: 40px; + min-width: 40px; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts new file mode 100644 index 0000000000..a4824fd51e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component.ts @@ -0,0 +1,201 @@ +/// +/// 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. +/// + +import { + ChangeDetectorRef, + Component, + EventEmitter, + forwardRef, + Input, + OnInit, + Output, + Renderer2, + ViewContainerRef, + ViewEncapsulation +} from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { + AxisPosition, + timeSeriesAxisPositionTranslations, + TimeSeriesChartYAxisSettings +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { MatButton } from '@angular/material/button'; +import { TbPopoverService } from '@shared/components/popover.service'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { + TimeSeriesChartYAxisSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-settings-panel.component'; +import { deepClone } from '@core/utils'; + +@Component({ + selector: 'tb-time-series-chart-y-axis-row', + templateUrl: './time-series-chart-y-axis-row.component.html', + styleUrls: ['./time-series-chart-y-axis-row.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TimeSeriesChartYAxisRowComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class TimeSeriesChartYAxisRowComponent implements ControlValueAccessor, OnInit { + + axisPositions = [AxisPosition.left, AxisPosition.right]; + + timeSeriesAxisPositionTranslations = timeSeriesAxisPositionTranslations; + + @Input() + disabled: boolean; + + @Input() + @coerceBoolean() + advanced = false; + + @Output() + axisRemoved = new EventEmitter(); + + axisFormGroup: UntypedFormGroup; + + modelValue: TimeSeriesChartYAxisSettings; + + private propagateChange = (_val: any) => {}; + + constructor(private fb: UntypedFormBuilder, + private popoverService: TbPopoverService, + private renderer: Renderer2, + private viewContainerRef: ViewContainerRef, + private cd: ChangeDetectorRef) { + } + + ngOnInit() { + this.axisFormGroup = this.fb.group({ + label: [null, []], + position: [null, []], + units: [null, []], + decimals: [null, []], + min: [null, []], + max: [null, []], + show: [null, []] + }); + this.axisFormGroup.valueChanges.subscribe( + () => this.updateModel() + ); + this.axisFormGroup.get('show').valueChanges.subscribe(() => { + this.updateValidators(); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.axisFormGroup.disable({emitEvent: false}); + } else { + this.axisFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: TimeSeriesChartYAxisSettings): void { + this.modelValue = value; + this.axisFormGroup.patchValue( + { + label: value.label, + position: value.position, + units: value.units, + decimals: value.decimals, + min: value.min, + max: value.max, + show: value.show, + }, {emitEvent: false} + ); + this.updateValidators(); + this.cd.markForCheck(); + } + + editAxis($event: Event, matButton: MatButton) { + if ($event) { + $event.stopPropagation(); + } + const trigger = matButton._elementRef.nativeElement; + if (this.popoverService.hasPopover(trigger)) { + this.popoverService.hidePopover(trigger); + } else { + const ctx: any = { + yAxisSettings: deepClone(this.modelValue), + advanced: this.advanced + }; + const yAxisSettingsPanelPopover = this.popoverService.displayPopover(trigger, this.renderer, + this.viewContainerRef, TimeSeriesChartYAxisSettingsPanelComponent, ['leftOnly', 'leftTopOnly', 'leftBottomOnly'], true, null, + ctx, + {}, + {}, {}, true); + yAxisSettingsPanelPopover.tbComponentRef.instance.popover = yAxisSettingsPanelPopover; + yAxisSettingsPanelPopover.tbComponentRef.instance.yAxisSettingsApplied.subscribe((yAxisSettings) => { + yAxisSettingsPanelPopover.hide(); + this.modelValue = {...this.modelValue, ...yAxisSettings}; + this.axisFormGroup.patchValue( + { + label: this.modelValue.label, + position: this.modelValue.position, + units: this.modelValue.units, + decimals: this.modelValue.decimals, + min: this.modelValue.min, + max: this.modelValue.max, + show: this.modelValue.show + }, + {emitEvent: false}); + this.updateValidators(); + this.propagateChange(this.modelValue); + }); + } + } + + private updateValidators() { + const show: boolean = this.axisFormGroup.get('show').value; + if (show) { + this.axisFormGroup.get('label').enable({emitEvent: false}); + this.axisFormGroup.get('position').enable({emitEvent: false}); + this.axisFormGroup.get('units').enable({emitEvent: false}); + this.axisFormGroup.get('decimals').enable({emitEvent: false}); + } else { + this.axisFormGroup.get('label').disable({emitEvent: false}); + this.axisFormGroup.get('position').disable({emitEvent: false}); + this.axisFormGroup.get('units').disable({emitEvent: false}); + this.axisFormGroup.get('decimals').disable({emitEvent: false}); + } + } + + private updateModel() { + const value = this.axisFormGroup.value; + this.modelValue.label = value.label; + this.modelValue.position = value.position; + this.modelValue.units = value.units; + this.modelValue.decimals = value.decimals; + this.modelValue.min = value.min; + this.modelValue.max = value.max; + this.modelValue.show = value.show; + this.propagateChange(this.modelValue); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-settings-panel.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-settings-panel.component.html new file mode 100644 index 0000000000..759074b4be --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-settings-panel.component.html @@ -0,0 +1,43 @@ + +
+
{{ 'widgets.time-series-chart.axis.y-axis-settings' | translate }}
+
+ + +
+
+ + +
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-settings-panel.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-settings-panel.component.scss new file mode 100644 index 0000000000..095c38b7f7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-settings-panel.component.scss @@ -0,0 +1,49 @@ +/** + * 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. + */ +@import '../../../../../../../../../scss/constants'; + +.tb-y-axis-settings-panel { + width: 530px; + display: flex; + flex-direction: column; + gap: 16px; + @media #{$mat-lt-md} { + width: 90vw; + } + .tb-y-axis-settings-panel-content { + display: flex; + flex-direction: column; + gap: 16px; + overflow: auto; + margin: -10px; + padding: 10px; + } + .tb-y-axis-settings-title { + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0.25px; + color: rgba(0, 0, 0, 0.87); + } + .tb-y-axis-settings-panel-buttons { + height: 40px; + display: flex; + flex-direction: row; + gap: 16px; + justify-content: flex-end; + align-items: flex-end; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-settings-panel.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-settings-panel.component.ts new file mode 100644 index 0000000000..8046aafd29 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-settings-panel.component.ts @@ -0,0 +1,66 @@ +/// +/// 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. +/// + +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { TbPopoverComponent } from '@shared/components/popover.component'; +import { UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { TimeSeriesChartYAxisSettings } from '@home/components/widget/lib/chart/time-series-chart.models'; +import { coerceBoolean } from '@shared/decorators/coercion'; + +@Component({ + selector: 'tb-time-series-chart-y-axis-settings-panel', + templateUrl: './time-series-chart-y-axis-settings-panel.component.html', + providers: [], + styleUrls: ['./time-series-chart-y-axis-settings-panel.component.scss'], + encapsulation: ViewEncapsulation.None +}) +export class TimeSeriesChartYAxisSettingsPanelComponent implements OnInit { + + @Input() + yAxisSettings: TimeSeriesChartYAxisSettings; + + @Input() + @coerceBoolean() + advanced = false; + + @Input() + popover: TbPopoverComponent; + + @Output() + yAxisSettingsApplied = new EventEmitter(); + + yAxisSettingsFormGroup: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + } + + ngOnInit(): void { + this.yAxisSettingsFormGroup = this.fb.group( + { + yAxis: [this.yAxisSettings, []] + } + ); + } + + cancel() { + this.popover?.hide(); + } + + applyYAxisSettings() { + const yAxisSettings = this.yAxisSettingsFormGroup.get('yAxis').getRawValue(); + this.yAxisSettingsApplied.emit(yAxisSettings); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.html new file mode 100644 index 0000000000..239c6ea18c --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.html @@ -0,0 +1,75 @@ + +
+
+
widgets.time-series-chart.no-aggregation-bar-width-strategy
+ + + {{ timeSeriesChartNoAggregationBarWidthStrategyTranslations.get(strategy) | translate }} + + +
+ + + + + + + + +
+ +
+
{{ label }}
+
+ + + + {{ (formGroup.get('relative').value ? + 'widgets.time-series-chart.bar-width-relative' : + 'widgets.time-series-chart.bar-width-absolute') | translate }} + + + {{ 'widgets.time-series-chart.bar-width-relative' | translate }} + + + {{ 'widgets.time-series-chart.bar-width-absolute' | translate }} + + + + + + % + + + + ms + +
+
+
diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts new file mode 100644 index 0000000000..36d8cb917a --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component.ts @@ -0,0 +1,147 @@ +/// +/// 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. +/// + +import { Component, forwardRef, Input, OnInit } from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormGroup, + Validators +} from '@angular/forms'; +import { + TimeSeriesChartNoAggregationBarWidthSettings, + timeSeriesChartNoAggregationBarWidthStrategies, + TimeSeriesChartNoAggregationBarWidthStrategy, + timeSeriesChartNoAggregationBarWidthStrategyTranslations +} from '@home/components/widget/lib/chart/time-series-chart.models'; +import { merge } from 'rxjs'; + +@Component({ + selector: 'tb-time-series-no-aggregation-bar-width-settings', + templateUrl: './time-series-no-aggregation-bar-width-settings.component.html', + styleUrls: ['./../../widget-settings.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => TimeSeriesNoAggregationBarWidthSettingsComponent), + multi: true + } + ] +}) +export class TimeSeriesNoAggregationBarWidthSettingsComponent implements OnInit, ControlValueAccessor { + + TimeSeriesChartNoAggregationBarWidthStrategy = TimeSeriesChartNoAggregationBarWidthStrategy; + + timeSeriesChartNoAggregationBarWidthStrategies = timeSeriesChartNoAggregationBarWidthStrategies; + + timeSeriesChartNoAggregationBarWidthStrategyTranslations = timeSeriesChartNoAggregationBarWidthStrategyTranslations; + + @Input() + disabled: boolean; + + private modelValue: TimeSeriesChartNoAggregationBarWidthSettings; + + private propagateChange = null; + + public barWidthSettingsFormGroup: UntypedFormGroup; + + constructor(private fb: UntypedFormBuilder) { + } + + ngOnInit(): void { + this.barWidthSettingsFormGroup = this.fb.group({ + strategy: [null, []], + groupWidth: this.fb.group({ + relative: [null, []], + relativeWidth: [null, [Validators.required, Validators.min(0.1), Validators.max(100)]], + absoluteWidth: [null, [Validators.required, Validators.min(100)]] + }), + barWidth: this.fb.group({ + relative: [null, []], + relativeWidth: [null, [Validators.required, Validators.min(0.1), Validators.max(100)]], + absoluteWidth: [null, [Validators.required, Validators.min(100)]] + }) + }); + this.barWidthSettingsFormGroup.valueChanges.subscribe(() => { + this.updateModel(); + }); + merge(this.barWidthSettingsFormGroup.get('strategy').valueChanges, + this.barWidthSettingsFormGroup.get('groupWidth.relative').valueChanges, + this.barWidthSettingsFormGroup.get('barWidth.relative').valueChanges) + .subscribe(() => { + this.updateValidators(); + }); + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(_fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.barWidthSettingsFormGroup.disable({emitEvent: false}); + } else { + this.barWidthSettingsFormGroup.enable({emitEvent: false}); + this.updateValidators(); + } + } + + writeValue(value: TimeSeriesChartNoAggregationBarWidthSettings): void { + this.modelValue = value; + this.barWidthSettingsFormGroup.patchValue( + value, {emitEvent: false} + ); + this.updateValidators(); + } + + private updateValidators() { + const strategy: TimeSeriesChartNoAggregationBarWidthStrategy = + this.barWidthSettingsFormGroup.get('strategy').value; + const groupWidthRelative: boolean = this.barWidthSettingsFormGroup.get('groupWidth.relative').value; + const barWidthRelative: boolean = this.barWidthSettingsFormGroup.get('barWidth.relative').value; + if (strategy === TimeSeriesChartNoAggregationBarWidthStrategy.group) { + this.barWidthSettingsFormGroup.get('groupWidth').enable({emitEvent: false}); + this.barWidthSettingsFormGroup.get('barWidth').disable({emitEvent: false}); + if (groupWidthRelative) { + this.barWidthSettingsFormGroup.get('groupWidth').get('relativeWidth').enable({emitEvent: false}); + this.barWidthSettingsFormGroup.get('groupWidth').get('absoluteWidth').disable({emitEvent: false}); + } else { + this.barWidthSettingsFormGroup.get('groupWidth').get('relativeWidth').disable({emitEvent: false}); + this.barWidthSettingsFormGroup.get('groupWidth').get('absoluteWidth').enable({emitEvent: false}); + } + } else if (strategy === TimeSeriesChartNoAggregationBarWidthStrategy.separate) { + this.barWidthSettingsFormGroup.get('groupWidth').disable({emitEvent: false}); + this.barWidthSettingsFormGroup.get('barWidth').enable({emitEvent: false}); + if (barWidthRelative) { + this.barWidthSettingsFormGroup.get('barWidth').get('relativeWidth').enable({emitEvent: false}); + this.barWidthSettingsFormGroup.get('barWidth').get('absoluteWidth').disable({emitEvent: false}); + } else { + this.barWidthSettingsFormGroup.get('barWidth').get('relativeWidth').disable({emitEvent: false}); + this.barWidthSettingsFormGroup.get('barWidth').get('absoluteWidth').enable({emitEvent: false}); + } + } + } + + private updateModel() { + this.modelValue = this.barWidthSettingsFormGroup.getRawValue(); + this.propagateChange(this.modelValue); + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.html new file mode 100644 index 0000000000..94831b530f --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.html @@ -0,0 +1,162 @@ + + + + +
+
+
+ + notifications + + + timeline + +
+
+ + + +
+
+ + +
+
+ +
+ + warning + + + + + + notifications + + + timeline + + + + + +
+
+ entity.no-keys-found +
+ + + {{ translate.get('entity.no-key-matching', + {key: truncate.transform(keySearchText, true, 6, '...')}) | async }} + + + entity.create-new-key + + + {{'entity.create-new-key' | translate }} + notifications + + + timeline + + +
+
+
+
+ + + f() + + + + + + + + {{ modelValue?.aggregationType }}({{ modelValue?.name }}) + + + {{modelValue?.name}} + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.scss new file mode 100644 index 0000000000..6d443d0057 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.scss @@ -0,0 +1,46 @@ +/** + * 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. + */ +.tb-data-key-input { + .mat-mdc-form-field.tb-inline-field.tb-key-field { + width: 100%; + .mat-mdc-text-field-wrapper:not(.mdc-text-field--outlined) { + padding-left: 8px; + padding-right: 0; + + .mat-mdc-form-field-infix { + padding-top: 0; + padding-bottom: 6px; + + .mdc-evolution-chip-set .mdc-evolution-chip { + margin: 0; + } + + input.mat-mdc-chip-input { + height: 32px; + margin-left: 0; + } + } + } + + .mat-mdc-chip.mat-mdc-standard-chip.tb-datakey-chip { + .tb-attribute-chip { + .tb-chip-labels { + background: transparent; + } + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.ts new file mode 100644 index 0000000000..d26f259185 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/data-key-input.component.ts @@ -0,0 +1,401 @@ +/// +/// 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. +/// + +import { + ChangeDetectorRef, + Component, + ElementRef, + EventEmitter, + forwardRef, + HostBinding, + Input, + OnChanges, + OnInit, + Output, + SimpleChanges, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormControl, + Validators +} from '@angular/forms'; +import { DataKeyType } from '@shared/models/telemetry/telemetry.models'; +import { COMMA, ENTER, SEMICOLON } from '@angular/cdk/keycodes'; +import { MatAutocomplete, MatAutocompleteTrigger } from '@angular/material/autocomplete'; +import { MatChipGrid, MatChipInputEvent } from '@angular/material/chips'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { DataKey, DatasourceType, widgetType } from '@shared/models/widget.models'; +import { Observable, of } from 'rxjs'; +import { MatDialog } from '@angular/material/dialog'; +import { TranslateService } from '@ngx-translate/core'; +import { TruncatePipe } from '@shared/pipe/truncate.pipe'; +import { UtilsService } from '@core/services/utils.service'; +import { alarmFields } from '@shared/models/alarm.models'; +import { filter, map, mergeMap, publishReplay, refCount, share, tap } from 'rxjs/operators'; +import { AggregationType } from '@shared/models/time/time.models'; +import { DataKeysCallbacks } from '@home/components/widget/config/data-keys.component.models'; +import { IAliasController } from '@core/api/widget-api.models'; + +@Component({ + selector: 'tb-data-key-input', + templateUrl: './data-key-input.component.html', + styleUrls: ['./data-key-input.component.scss', '../../../config/data-keys.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DataKeyInputComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class DataKeyInputComponent implements ControlValueAccessor, OnInit, OnChanges { + + @HostBinding('class') + hostClass = 'tb-data-key-input'; + + DataKeyType = DataKeyType; + + separatorKeysCodes: number[] = [ENTER, COMMA, SEMICOLON]; + + @ViewChild('keyInput') keyInput: ElementRef; + @ViewChild('keyAutocomplete') matAutocomplete: MatAutocomplete; + @ViewChild(MatAutocompleteTrigger) autocomplete: MatAutocompleteTrigger; + @ViewChild('chipList') chipList: MatChipGrid; + + @Input() + disabled: boolean; + + @Input() + @coerceBoolean() + required = false; + + @Input() + requiredText: string; + + @Input() + @coerceBoolean() + isLatestDataKeys = false; + + @Input() + @coerceBoolean() + editable = true; + + @Input() + datasourceType: DatasourceType; + + @Input() + entityAliasId: string; + + @Input() + entityAlias: string; + + @Input() + deviceId: string; + + @Input() + widgetType: widgetType; + + @Input() + callbacks: DataKeysCallbacks; + + @Input() + aliasController: IAliasController; + + @Input() + dataKeyType: DataKeyType; + + @Input() + dataKeyTypes: DataKeyType[]; + + @Input() + generateKey: (key: DataKey) => DataKey = (key) => key; + + @Output() + keyEdit = new EventEmitter(); + + keysFormControl: UntypedFormControl; + + keyFormControl: UntypedFormControl; + + modelValue: DataKey; + + filteredKeys: Observable>; + + keySearchText = ''; + + alarmKeys: Array; + functionTypeKeys: Array; + + allowedDataKeyTypes: DataKeyType[] = []; + + private latestKeySearchTextResult: Array = null; + private keyFetchObservable$: Observable> = null; + + get isEntityDatasource(): boolean { + return [DatasourceType.device, DatasourceType.entity].includes(this.datasourceType); + } + + private propagateChange = (_val: any) => {}; + + constructor(private fb: UntypedFormBuilder, + private dialog: MatDialog, + private cd: ChangeDetectorRef, + public translate: TranslateService, + public truncate: TruncatePipe, + private utils: UtilsService) { + } + + ngOnInit() { + this.alarmKeys = []; + for (const name of Object.keys(alarmFields)) { + this.alarmKeys.push({ + name, + type: DataKeyType.alarm + }); + } + this.functionTypeKeys = []; + for (const type of this.utils.getPredefinedFunctionsList()) { + this.functionTypeKeys.push({ + name: type, + type: DataKeyType.function + }); + } + this.keyFormControl = this.fb.control(''); + this.keysFormControl = this.fb.control([], this.required ? [Validators.required] : []); + this.filteredKeys = this.keyFormControl.valueChanges + .pipe( + tap((value: string | DataKey) => { + if (value && typeof value !== 'string') { + this.addKeyFromChipValue(value); + } else if (value === null) { + this.clearKeyChip(this.keyInput.nativeElement.value); + } + }), + filter((value) => typeof value === 'string'), + map((value) => value ? (typeof value === 'string' ? value : value.name) : ''), + mergeMap(name => this.fetchKeys(name) ), + share() + ); + this.updateAllowedDataKeys(); + } + + private updateAllowedDataKeys() { + this.allowedDataKeyTypes.length = 0; + if (this.dataKeyTypes?.length) { + this.allowedDataKeyTypes = this.allowedDataKeyTypes.concat(this.dataKeyTypes); + } else { + this.allowedDataKeyTypes = [DataKeyType.timeseries]; + if (this.isLatestDataKeys || this.widgetType === widgetType.latest || this.widgetType === widgetType.alarm) { + this.allowedDataKeyTypes.push(DataKeyType.attribute); + this.allowedDataKeyTypes.push(DataKeyType.entityField); + if (this.widgetType === widgetType.alarm) { + this.allowedDataKeyTypes.push(DataKeyType.alarm); + } + } + } + } + + private reset() { + if (this.keyInput) { + this.keyInput.nativeElement.value = ''; + } + this.keyFormControl.patchValue('', {emitEvent: false}); + this.latestKeySearchTextResult = null; + } + + ngOnChanges(changes: SimpleChanges): void { + for (const propName of Object.keys(changes)) { + const change = changes[propName]; + if (!change.firstChange && change.currentValue !== change.previousValue) { + if (['deviceId', 'entityAliasId', 'entityAlias', 'isLatestDataKeys'].includes(propName)) { + this.clearKeySearchCache(); + if (propName === 'isLatestDataKeys') { + this.updateAllowedDataKeys(); + if (!this.isLatestDataKeys) { + if (this.widgetType === widgetType.timeseries && + this.modelValue?.type && + this.modelValue.type !== DataKeyType.timeseries) { + setTimeout(() => { + this.modelValue = null; + this.updateModel(); + this.clearKeyChip('', false); + }, 1); + } + } + } + } else if (['datasourceType'].includes(propName)) { + if ([DatasourceType.device, DatasourceType.entity].includes(change.previousValue) && + [DatasourceType.device, DatasourceType.entity].includes(change.currentValue)) { + this.clearKeySearchCache(); + } else { + this.clearKeySearchCache(); + setTimeout(() => { + this.reset(); + }, 1); + } + } + } + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.keysFormControl.disable({emitEvent: false}); + } else { + this.keysFormControl.enable({emitEvent: false}); + } + } + + writeValue(value: DataKey): void { + this.modelValue = (value?.name && value?.type) ? value : null; + this.keysFormControl.patchValue(this.modelValue ? [this.modelValue] : [], {emitEvent: false}); + this.cd.markForCheck(); + } + + dataKeyHasAggregation(): boolean { + return this.widgetType === widgetType.latest && this.modelValue?.type === DataKeyType.timeseries + && this.modelValue?.aggregationType && this.modelValue?.aggregationType !== AggregationType.NONE; + } + + dataKeyHasPostprocessing(): boolean { + return !!this.modelValue?.postFuncBody; + } + + displayKeyFn(key?: DataKey): string | undefined { + return key ? key.name : undefined; + } + + createKey(name: string, dataKeyType: DataKeyType = this.dataKeyType) { + this.addKeyFromChipValue({name: name ? name.trim() : '', type: dataKeyType}); + } + + addKey(event: MatChipInputEvent): void { + const value = event.value; + if ((value || '').trim() && this.dataKeyType) { + this.addKeyFromChipValue({name: value.trim(), type: this.dataKeyType}); + } else { + this.clearKeyChip(); + } + } + + editKey() { + this.keyEdit.emit(this.modelValue); + } + + removeKey() { + this.modelValue = null; + this.updateModel(); + this.clearKeyChip(); + } + + textIsNotEmpty(text: string): boolean { + return text && text.length > 0; + } + + clearKeyChip(value: string = '', focus = true) { + this.autocomplete.closePanel(); + this.keyInput.nativeElement.value = value; + this.keyFormControl.patchValue(value, {emitEvent: focus}); + if (focus) { + setTimeout(() => { + this.keyInput.nativeElement.blur(); + this.keyInput.nativeElement.focus(); + }, 0); + } + } + + onKeyInputFocus() { + if (!this.modelValue?.type) { + this.keyFormControl.updateValueAndValidity({onlySelf: true, emitEvent: true}); + } + } + + private fetchKeys(searchText?: string): Observable> { + if (this.keySearchText !== searchText || this.latestKeySearchTextResult === null) { + this.keySearchText = searchText; + const dataKeyFilter = this.createDataKeyFilter(this.keySearchText); + return this.getKeys().pipe( + map(name => name.filter(dataKeyFilter)), + tap(res => this.latestKeySearchTextResult = res) + ); + } + return of(this.latestKeySearchTextResult); + } + + private getKeys(): Observable> { + if (this.keyFetchObservable$ === null) { + let fetchObservable: Observable>; + if (this.datasourceType === DatasourceType.function) { + const targetKeysList = this.widgetType === widgetType.alarm ? this.alarmKeys : this.functionTypeKeys; + fetchObservable = of(targetKeysList); + } else if (this.datasourceType === DatasourceType.entity && (this.entityAliasId || this.entityAlias) || + this.datasourceType === DatasourceType.device && this.deviceId) { + if (this.datasourceType === DatasourceType.device) { + fetchObservable = this.callbacks.fetchEntityKeysForDevice(this.deviceId, this.allowedDataKeyTypes); + } else { + let entityAliasId = this.entityAliasId; + if (!entityAliasId && this.entityAlias && this.aliasController) { + entityAliasId = this.aliasController.getEntityAliasId(this.entityAlias); + } + fetchObservable = entityAliasId ? this.callbacks.fetchEntityKeys(entityAliasId, this.allowedDataKeyTypes) : of([]); + } + } else { + fetchObservable = of([]); + } + this.keyFetchObservable$ = fetchObservable.pipe( + publishReplay(1), + refCount() + ); + } + return this.keyFetchObservable$; + } + + private createDataKeyFilter(query: string): (key: DataKey) => boolean { + const lowercaseQuery = query.toLowerCase(); + return key => key.name.toLowerCase().startsWith(lowercaseQuery); + } + + private addKeyFromChipValue(chip: DataKey) { + this.modelValue = this.generateKey(chip); + this.updateModel(); + this.clearKeyChip('', false); + } + + private clearKeySearchCache() { + this.keySearchText = ''; + this.keyFetchObservable$ = null; + this.latestKeySearchTextResult = null; + } + + private updateModel() { + this.keysFormControl.patchValue(this.modelValue ? [this.modelValue] : [], {emitEvent: false}); + this.propagateChange(this.modelValue); + } + +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/entity-alias-input.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/entity-alias-input.component.html new file mode 100644 index 0000000000..78d0533865 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/entity-alias-input.component.html @@ -0,0 +1,49 @@ + + + + + warning + + + + + + + + diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/entity-alias-input.component.scss b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/entity-alias-input.component.scss new file mode 100644 index 0000000000..baccbf2ab7 --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/entity-alias-input.component.scss @@ -0,0 +1,20 @@ +/** + * 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. + */ +.tb-entity-alias-input { + .mat-mdc-form-field.tb-inline-field { + width: 100%; + } +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/entity-alias-input.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/entity-alias-input.component.ts new file mode 100644 index 0000000000..dd6d19f14e --- /dev/null +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/entity-alias-input.component.ts @@ -0,0 +1,156 @@ +/// +/// 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. +/// + +import { + ChangeDetectorRef, + Component, + ElementRef, + forwardRef, + HostBinding, + Input, + OnInit, + ViewChild, + ViewEncapsulation +} from '@angular/core'; +import { + ControlValueAccessor, + NG_VALUE_ACCESSOR, + UntypedFormBuilder, + UntypedFormControl, + Validators +} from '@angular/forms'; +import { coerceBoolean } from '@shared/decorators/coercion'; +import { IAliasController } from '@core/api/widget-api.models'; +import { map, mergeMap } from 'rxjs/operators'; +import { Observable, of } from 'rxjs'; +import { TimeSeriesChartThresholdType } from '@home/components/widget/lib/chart/time-series-chart.models'; + +@Component({ + selector: 'tb-entity-alias-input', + templateUrl: './entity-alias-input.component.html', + styleUrls: ['./entity-alias-input.component.scss'], + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => EntityAliasInputComponent), + multi: true + } + ], + encapsulation: ViewEncapsulation.None +}) +export class EntityAliasInputComponent implements ControlValueAccessor, OnInit { + + @HostBinding('class') + hostClass = 'tb-entity-alias-input'; + + @ViewChild('entityAliasInput') entityAliasInput: ElementRef; + + @Input() + disabled: boolean; + + @Input() + @coerceBoolean() + required = false; + + @Input() + aliasController: IAliasController; + + entityAliasFormControl: UntypedFormControl; + + filteredEntityAliases: Observable>; + aliasSearchText = ''; + + private entityAliasList: Array = []; + private entityAliasDirty = false; + + private propagateChange = (_val: any) => {}; + + constructor(private fb: UntypedFormBuilder, + private cd: ChangeDetectorRef) { + } + + ngOnInit() { + this.entityAliasFormControl = this.fb.control(null, this.required ? [Validators.required] : []); + this.entityAliasFormControl.valueChanges.subscribe( + () => this.updateModel() + ); + + this.filteredEntityAliases = this.entityAliasFormControl.valueChanges + .pipe( + map(value => value ? value : ''), + mergeMap(name => this.fetchEntityAliases(name) ) + ); + + if (this.aliasController) { + const entityAliases = this.aliasController.getEntityAliases(); + for (const aliasId of Object.keys(entityAliases)) { + this.entityAliasList.push(entityAliases[aliasId].alias); + } + } + } + + registerOnChange(fn: any): void { + this.propagateChange = fn; + } + + registerOnTouched(fn: any): void { + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + if (isDisabled) { + this.entityAliasFormControl.disable({emitEvent: false}); + } else { + this.entityAliasFormControl.enable({emitEvent: false}); + } + } + + writeValue(value: string): void { + this.entityAliasFormControl.patchValue(value, {emitEvent: false}); + this.entityAliasDirty = true; + } + + onEntityAliasFocus() { + if (this.entityAliasDirty) { + this.entityAliasFormControl.updateValueAndValidity({onlySelf: true}); + this.entityAliasDirty = false; + } + } + + clearEntityAlias() { + this.entityAliasFormControl.patchValue(null, {emitEvent: true}); + setTimeout(() => { + this.entityAliasInput.nativeElement.blur(); + this.entityAliasInput.nativeElement.focus(); + }, 0); + } + + private fetchEntityAliases(searchText?: string): Observable> { + this.aliasSearchText = searchText; + let result = this.entityAliasList; + if (searchText && searchText.length) { + result = this.entityAliasList.filter((entityAlias) => entityAlias.toLowerCase().includes(searchText.toLowerCase())); + } + return of(result); + } + + private updateModel() { + const value = this.entityAliasFormControl.value; + this.propagateChange(value); + } + + protected readonly TimeSeriesChartThresholdType = TimeSeriesChartThresholdType; +} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.html b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.html index 44ab1d0bad..3dd5e08860 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.html @@ -16,7 +16,7 @@ --> -
+
{{ 'legend.direction' | translate }}
@@ -31,8 +31,9 @@ + [disabled]="!hideDirection && + legendConfigForm.get('direction').value === legendDirection.row && + (pos === legendPosition.left || pos === legendPosition.right)"> {{ legendPositionTranslations.get(legendPosition[pos]) | translate }} diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.ts index cf7b3d4e29..8b065247f3 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/legend-config.component.ts @@ -25,6 +25,7 @@ import { legendPositionTranslationMap } from '@shared/models/widget.models'; import { Subscription } from 'rxjs'; +import { coerceBoolean } from '@shared/decorators/coercion'; // @dynamic @Component({ @@ -43,6 +44,10 @@ export class LegendConfigComponent implements OnInit, OnDestroy, ControlValueAcc @Input() disabled: boolean; + @Input() + @coerceBoolean() + hideDirection = false; + legendConfigForm: UntypedFormGroup; legendDirection = LegendDirection; legendDirections = Object.keys(LegendDirection); @@ -60,15 +65,17 @@ export class LegendConfigComponent implements OnInit, OnDestroy, ControlValueAcc ngOnInit(): void { this.legendConfigForm = this.fb.group({ - direction: [null, []], position: [null, []], showValues: [[], []], sortDataKeys: [null, []] }); - this.legendSettingsFormDirectionChanges$ = this.legendConfigForm.get('direction').valueChanges + if (!this.hideDirection) { + this.legendConfigForm.addControl('direction', this.fb.control([null, []])); + this.legendSettingsFormDirectionChanges$ = this.legendConfigForm.get('direction').valueChanges .subscribe((direction: LegendDirection) => { this.onDirectionChanged(direction); }); + } this.legendSettingsFormChanges$ = this.legendConfigForm.valueChanges.subscribe( () => this.legendConfigUpdated() ); @@ -114,23 +121,30 @@ export class LegendConfigComponent implements OnInit, OnDestroy, ControlValueAcc writeValue(legendConfig: LegendConfig): void { if (legendConfig) { - this.legendConfigForm.patchValue({ - direction: legendConfig.direction, + const value: any = { position: legendConfig.position, showValues: this.getShowValues(legendConfig), sortDataKeys: isDefined(legendConfig.sortDataKeys) ? legendConfig.sortDataKeys : false - }, {emitEvent: false}); + }; + if (!this.hideDirection) { + value.direction = legendConfig.direction; + } + this.legendConfigForm.patchValue(value, {emitEvent: false}); + } + if (!this.hideDirection) { + this.onDirectionChanged(legendConfig?.direction); } - this.onDirectionChanged(legendConfig.direction); } private legendConfigUpdated() { const configValue = this.legendConfigForm.value; const legendConfig: Partial = { - direction: configValue.direction, position: configValue.position, sortDataKeys: configValue.sortDataKeys }; + if (!this.hideDirection) { + legendConfig.direction = configValue.direction; + } this.setShowValues(configValue.showValues, legendConfig); this.propagateChange(legendConfig); } diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts index e39c079f75..b80b20f402 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/common/widget-settings-common.module.ts @@ -92,6 +92,35 @@ import { import { WidgetButtonCustomStylePanelComponent } from '@home/components/widget/lib/settings/common/button/widget-button-custom-style-panel.component'; +import { + TimeSeriesChartAxisSettingsComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-chart-axis-settings.component'; +import { + TimeSeriesChartThresholdsPanelComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-chart-thresholds-panel.component'; +import { + TimeSeriesChartThresholdRowComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-chart-threshold-row.component'; +import { DataKeyInputComponent } from '@home/components/widget/lib/settings/common/data-key-input.component'; +import { EntityAliasInputComponent } from '@home/components/widget/lib/settings/common/entity-alias-input.component'; +import { + TimeSeriesChartThresholdSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-chart-threshold-settings-panel.component'; +import { + TimeSeriesNoAggregationBarWidthSettingsComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-no-aggregation-bar-width-settings.component'; +import { + TimeSeriesChartYAxesPanelComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-chart-y-axes-panel.component'; +import { + TimeSeriesChartYAxisRowComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-row.component'; +import { + TimeSeriesChartYAxisSettingsPanelComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-chart-y-axis-settings-panel.component'; +import { + TimeSeriesChartAnimationSettingsComponent +} from '@home/components/widget/lib/settings/common/chart/time-series-chart-animation-settings.component'; @NgModule({ declarations: [ @@ -127,7 +156,18 @@ import { WidgetActionSettingsPanelComponent, WidgetButtonAppearanceComponent, WidgetButtonCustomStyleComponent, - WidgetButtonCustomStylePanelComponent + WidgetButtonCustomStylePanelComponent, + TimeSeriesChartAxisSettingsComponent, + TimeSeriesChartThresholdsPanelComponent, + TimeSeriesChartThresholdRowComponent, + TimeSeriesChartThresholdSettingsPanelComponent, + TimeSeriesNoAggregationBarWidthSettingsComponent, + TimeSeriesChartYAxesPanelComponent, + TimeSeriesChartYAxisRowComponent, + TimeSeriesChartYAxisSettingsPanelComponent, + TimeSeriesChartAnimationSettingsComponent, + DataKeyInputComponent, + EntityAliasInputComponent ], imports: [ CommonModule, @@ -167,7 +207,18 @@ import { WidgetActionSettingsPanelComponent, WidgetButtonAppearanceComponent, WidgetButtonCustomStyleComponent, - WidgetButtonCustomStylePanelComponent + WidgetButtonCustomStylePanelComponent, + TimeSeriesChartAxisSettingsComponent, + TimeSeriesChartThresholdsPanelComponent, + TimeSeriesChartThresholdRowComponent, + TimeSeriesChartThresholdSettingsPanelComponent, + TimeSeriesNoAggregationBarWidthSettingsComponent, + TimeSeriesChartYAxesPanelComponent, + TimeSeriesChartYAxisRowComponent, + TimeSeriesChartYAxisSettingsPanelComponent, + TimeSeriesChartAnimationSettingsComponent, + DataKeyInputComponent, + EntityAliasInputComponent ], providers: [ ColorSettingsComponentService, diff --git a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts index 2943b4cc09..8522734eda 100644 --- a/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/lib/settings/widget-settings.module.ts @@ -330,6 +330,21 @@ import { import { ToggleButtonWidgetSettingsComponent } from '@home/components/widget/lib/settings/button/toggle-button-widget-settings.component'; +import { + TimeSeriesChartKeySettingsComponent +} from '@home/components/widget/lib/settings/chart/time-series-chart-key-settings.component'; +import { + TimeSeriesChartLineSettingsComponent +} from '@home/components/widget/lib/settings/chart/time-series-chart-line-settings.component'; +import { + TimeSeriesChartFillSettingsComponent +} from '@home/components/widget/lib/settings/chart/time-series-chart-fill-settings.component'; +import { + TimeSeriesChartBarSettingsComponent +} from '@home/components/widget/lib/settings/chart/time-series-chart-bar-settings.component'; +import { + TimeSeriesChartWidgetSettingsComponent +} from '@home/components/widget/lib/settings/chart/time-series-chart-widget-settings.component'; @NgModule({ declarations: [ @@ -448,7 +463,12 @@ import { CommandButtonWidgetSettingsComponent, PowerButtonWidgetSettingsComponent, SliderWidgetSettingsComponent, - ToggleButtonWidgetSettingsComponent + ToggleButtonWidgetSettingsComponent, + TimeSeriesChartKeySettingsComponent, + TimeSeriesChartLineSettingsComponent, + TimeSeriesChartBarSettingsComponent, + TimeSeriesChartFillSettingsComponent, + TimeSeriesChartWidgetSettingsComponent ], imports: [ CommonModule, @@ -572,7 +592,12 @@ import { CommandButtonWidgetSettingsComponent, PowerButtonWidgetSettingsComponent, SliderWidgetSettingsComponent, - ToggleButtonWidgetSettingsComponent + ToggleButtonWidgetSettingsComponent, + TimeSeriesChartKeySettingsComponent, + TimeSeriesChartLineSettingsComponent, + TimeSeriesChartBarSettingsComponent, + TimeSeriesChartFillSettingsComponent, + TimeSeriesChartWidgetSettingsComponent ] }) export class WidgetSettingsModule { @@ -663,5 +688,7 @@ export const widgetSettingsComponentsMap: {[key: string]: Type { + (window as any).TbTimeSeriesChart = mod.TbTimeSeriesChart; + })) + ); widgetModulesTasks.push(from(import('@home/components/widget/lib/analogue-compass')).pipe( tap((mod) => { (window as any).TbAnalogueCompass = mod.TbAnalogueCompass; @@ -577,6 +583,9 @@ export class WidgetComponentService { if (!isFunction(result.typeParameters.defaultLatestDataKeysFunction)) { result.typeParameters.defaultLatestDataKeysFunction = null; } + if (!isFunction(result.typeParameters.dataKeySettingsFunction)) { + result.typeParameters.dataKeySettingsFunction = null; + } if (isUndefined(result.typeParameters.displayRpcMessageToast)) { result.typeParameters.displayRpcMessageToast = true; } diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts index 03bd97dd41..0ae45d1eff 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-components.module.ts @@ -76,6 +76,7 @@ import { CommandButtonWidgetComponent } from '@home/components/widget/lib/button import { PowerButtonWidgetComponent } from '@home/components/widget/lib/rpc/power-button-widget.component'; import { SliderWidgetComponent } from '@home/components/widget/lib/rpc/slider-widget.component'; import { ToggleButtonWidgetComponent } from '@home/components/widget/lib/button/toggle-button-widget.component'; +import { TimeSeriesChartWidgetComponent } from '@home/components/widget/lib/chart/time-series-chart-widget.component'; @NgModule({ declarations: @@ -124,7 +125,8 @@ import { ToggleButtonWidgetComponent } from '@home/components/widget/lib/button/ CommandButtonWidgetComponent, PowerButtonWidgetComponent, SliderWidgetComponent, - ToggleButtonWidgetComponent + ToggleButtonWidgetComponent, + TimeSeriesChartWidgetComponent ], imports: [ CommonModule, @@ -177,7 +179,8 @@ import { ToggleButtonWidgetComponent } from '@home/components/widget/lib/button/ CommandButtonWidgetComponent, PowerButtonWidgetComponent, SliderWidgetComponent, - ToggleButtonWidgetComponent + ToggleButtonWidgetComponent, + TimeSeriesChartWidgetComponent ], providers: [ {provide: WIDGET_COMPONENTS_MODULE_TOKEN, useValue: WidgetComponentsModule } diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html index 407ef94107..300a7f54d1 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html +++ b/ui-ngx/src/app/modules/home/components/widget/widget-config.component.html @@ -288,6 +288,7 @@
- - {{ actionTitle }} - +
+ + {{ actionTitle }} + +
diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-action-button-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-action-button-configuration.component.ts index af7e0a997b..e62d647694 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-action-button-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-action-button-configuration.component.ts @@ -52,6 +52,9 @@ export class NotificationActionButtonConfigurationComponent implements ControlVa @Input() actionTitle: string; + @Input() + sliderHint: string; + private hideButtonTextValue = false; get hideButtonText(): boolean { diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.html b/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.html index 0a992618b9..8297985a16 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.html +++ b/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.html @@ -120,10 +120,25 @@ }} -
+
+
+ + {{ 'icon.icon' | translate }} + +
+ + + + +
+
diff --git a/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts b/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts index 1f54784d64..0adb1a557f 100644 --- a/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts +++ b/ui-ngx/src/app/modules/home/pages/notification/template/configuration/notification-template-configuration.component.ts @@ -34,8 +34,9 @@ import { } from '@shared/models/notification.models'; import { takeUntil } from 'rxjs/operators'; import { Subject } from 'rxjs'; -import { isDefinedAndNotNull } from '@core/utils'; +import { deepClone, isDefinedAndNotNull } from '@core/utils'; import { coerceBoolean } from '@shared/decorators/coercion'; +import { TranslateService } from '@ngx-translate/core'; @Component({ selector: 'tb-template-configuration', @@ -96,7 +97,8 @@ export class NotificationTemplateConfigurationComponent implements OnDestroy, Co private propagateChange = (v: any) => { }; private readonly destroy$ = new Subject(); - constructor(private fb: FormBuilder) { + constructor(private fb: FormBuilder, + private translate: TranslateService) { this.templateConfigurationForm = this.buildForm(); this.templateConfigurationForm.valueChanges.pipe( takeUntil(this.destroy$) @@ -110,8 +112,16 @@ export class NotificationTemplateConfigurationComponent implements OnDestroy, Co this.destroy$.complete(); } - writeValue(value: any) { - this.templateConfigurationForm.patchValue(value, {emitEvent: false}); + writeValue(value: Partial) { + const settings = deepClone(value); + if (isDefinedAndNotNull(settings)) { + for (const method of Object.values(NotificationDeliveryMethod)) { + if (isDefinedAndNotNull(settings[method]?.enabled)) { + delete settings[method].enabled; + } + } + } + this.templateConfigurationForm.patchValue(settings, {emitEvent: false}); } registerOnChange(fn: any): void { @@ -137,6 +147,16 @@ export class NotificationTemplateConfigurationComponent implements OnDestroy, Co }; } + get hotificationTapActionHint(): string { + switch (this.notificationType) { + case NotificationType.ALARM: + case NotificationType.ALARM_ASSIGNMENT: + case NotificationType.ALARM_COMMENT: + return this.translate.instant('notification.notification-tap-action-hint'); + } + return ''; + } + private updateDisabledForms(){ Object.values(NotificationDeliveryMethod).forEach((method) => { const form = this.templateConfigurationForm.get(method); @@ -148,6 +168,9 @@ export class NotificationTemplateConfigurationComponent implements OnDestroy, Co case NotificationDeliveryMethod.WEB: form.get('additionalConfig.icon.enabled').updateValueAndValidity({onlySelf: true}); break; + case NotificationDeliveryMethod.MOBILE_APP: + form.get('additionalConfig.icon.enabled').updateValueAndValidity({onlySelf: true}); + break; } } }); @@ -213,9 +236,25 @@ export class NotificationTemplateConfigurationComponent implements OnDestroy, Co subject: ['', [Validators.required, Validators.maxLength(50)]], body: ['', [Validators.required, Validators.maxLength(150)]], additionalConfig: this.fb.group({ + icon: this.fb.group({ + enabled: [false], + icon: [{value: 'notifications', disabled: true}, Validators.required], + color: [{value: '#757575', disabled: true}] + }), onClick: [null] }) }); + deliveryMethodForm.get('additionalConfig.icon.enabled').valueChanges.pipe( + takeUntil(this.destroy$) + ).subscribe((value) => { + if (value) { + deliveryMethodForm.get('additionalConfig.icon.icon').enable({emitEvent: false}); + deliveryMethodForm.get('additionalConfig.icon.color').enable({emitEvent: false}); + } else { + deliveryMethodForm.get('additionalConfig.icon.icon').disable({emitEvent: false}); + deliveryMethodForm.get('additionalConfig.icon.color').disable({emitEvent: false}); + } + }); break; case NotificationDeliveryMethod.MICROSOFT_TEAMS: deliveryMethodForm = this.fb.group({ diff --git a/ui-ngx/src/app/shared/components/image/embed-image-dialog.component.scss b/ui-ngx/src/app/shared/components/image/embed-image-dialog.component.scss index eeaff6e83c..045f51b808 100644 --- a/ui-ngx/src/app/shared/components/image/embed-image-dialog.component.scss +++ b/ui-ngx/src/app/shared/components/image/embed-image-dialog.component.scss @@ -16,6 +16,7 @@ :host { .mat-mdc-dialog-content { display: flex; + width: 780px; flex-direction: column; gap: 16px; .tb-embed-image-text { diff --git a/ui-ngx/src/app/shared/components/image/image-dialog.component.ts b/ui-ngx/src/app/shared/components/image/image-dialog.component.ts index b140c6bf32..40e7d2ec89 100644 --- a/ui-ngx/src/app/shared/components/image/image-dialog.component.ts +++ b/ui-ngx/src/app/shared/components/image/image-dialog.component.ts @@ -66,7 +66,7 @@ export class ImageDialogComponent extends this.image = data.image; this.readonly = data.readonly; this.imagePreviewData = { - url: this.image.public ? this.image.publicLink : this.image.link + url: this.image.link }; } @@ -155,8 +155,6 @@ export class ImageDialogComponent extends let url; if (result.base64) { url = result.base64; - } else if (this.image.public) { - url = `${this.image.publicLink}?ts=${new Date().getTime()}`; } else { url = this.image.link; } diff --git a/ui-ngx/src/app/shared/components/toggle-select.component.ts b/ui-ngx/src/app/shared/components/toggle-select.component.ts index 9be788c08d..484ea19b2e 100644 --- a/ui-ngx/src/app/shared/components/toggle-select.component.ts +++ b/ui-ngx/src/app/shared/components/toggle-select.component.ts @@ -43,7 +43,7 @@ export class ToggleSelectComponent extends _ToggleBase implements ControlValueAc disabled: boolean; @Input() - selectMediaBreakpoint; + selectMediaBreakpoint: string; @Input() appearance: ToggleHeaderAppearance = 'stroked'; diff --git a/ui-ngx/src/app/shared/models/color.models.ts b/ui-ngx/src/app/shared/models/color.models.ts new file mode 100644 index 0000000000..7c1d08baf8 --- /dev/null +++ b/ui-ngx/src/app/shared/models/color.models.ts @@ -0,0 +1,24 @@ +/// +/// 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. +/// + +export interface TbColor { + light: string; + dark: string; +} + +export interface TbColorScheme { + [key: string]: TbColor; +} diff --git a/ui-ngx/src/app/shared/models/common.ts b/ui-ngx/src/app/shared/models/common.ts new file mode 100644 index 0000000000..c331cc5127 --- /dev/null +++ b/ui-ngx/src/app/shared/models/common.ts @@ -0,0 +1,19 @@ +/// +/// 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. +/// + +export type DeepPartial = T extends object ? { + [P in keyof T]?: DeepPartial; +} : T; diff --git a/ui-ngx/src/app/shared/models/limited-api.models.ts b/ui-ngx/src/app/shared/models/limited-api.models.ts index d0e2e0cb76..291af807da 100644 --- a/ui-ngx/src/app/shared/models/limited-api.models.ts +++ b/ui-ngx/src/app/shared/models/limited-api.models.ts @@ -26,7 +26,9 @@ export enum LimitedApi { TRANSPORT_MESSAGES_PER_TENANT = 'TRANSPORT_MESSAGES_PER_TENANT', TRANSPORT_MESSAGES_PER_DEVICE = 'TRANSPORT_MESSAGES_PER_DEVICE', EDGE_EVENTS = 'EDGE_EVENTS', - EDGE_EVENTS_PER_EDGE = 'EDGE_EVENTS_PER_EDGE' + EDGE_EVENTS_PER_EDGE = 'EDGE_EVENTS_PER_EDGE', + EDGE_UPLINK_MESSAGES = 'EDGE_UPLINK_MESSAGES', + EDGE_UPLINK_MESSAGES_PER_EDGE = 'EDGE_UPLINK_MESSAGES_PER_EDGE' } export const LimitedApiTranslationMap = new Map( @@ -43,5 +45,7 @@ export const LimitedApiTranslationMap = new Map( [LimitedApi.TRANSPORT_MESSAGES_PER_DEVICE, 'api-limit.transport-messages-per-device'], [LimitedApi.EDGE_EVENTS, 'api-limit.edge-events'], [LimitedApi.EDGE_EVENTS_PER_EDGE, 'api-limit.edge-events-per-edge'], + [LimitedApi.EDGE_UPLINK_MESSAGES, 'api-limit.edge-uplink-messages'], + [LimitedApi.EDGE_UPLINK_MESSAGES_PER_EDGE, 'api-limit.edge-uplink-messages-per-edge'] ] ); diff --git a/ui-ngx/src/app/shared/models/time/time.models.ts b/ui-ngx/src/app/shared/models/time/time.models.ts index 8d821b6689..deec2ee8f9 100644 --- a/ui-ngx/src/app/shared/models/time/time.models.ts +++ b/ui-ngx/src/app/shared/models/time/time.models.ts @@ -1116,8 +1116,13 @@ export const endIntervalDate = (current: moment_.Moment, interval: IntervalType) }; export const calculateAggIntervalWithSubscriptionTimeWindow - = (subsTw: SubscriptionTimewindow, endTs: number, timestamp: number): [number, number] => - calculateInterval(subsTw.startTs, endTs, subsTw.aggregation.interval, subsTw.tsOffset, subsTw.timezone, timestamp); + = (subsTw: SubscriptionTimewindow, endTs: number, timestamp: number): [number, number] => { + if (subsTw.aggregation.type === AggregationType.NONE) { + return [timestamp, timestamp]; + } else { + return calculateInterval(subsTw.startTs, endTs, subsTw.aggregation.interval, subsTw.tsOffset, subsTw.timezone, timestamp); + } +}; export const calculateAggIntervalWithWidgetTimeWindow = (widgetTimeWindow: WidgetTimewindow, timestamp: number): [number, number] => diff --git a/ui-ngx/src/app/shared/models/widget-settings.models.ts b/ui-ngx/src/app/shared/models/widget-settings.models.ts index 8e60c0d9cb..1e2e0d2415 100644 --- a/ui-ngx/src/app/shared/models/widget-settings.models.ts +++ b/ui-ngx/src/app/shared/models/widget-settings.models.ts @@ -348,7 +348,7 @@ export const customDateFormat = (format: string): DateFormatSettings => ({ custom: true }); -export const dateFormats = ['MMM dd yyyy HH:mm', 'dd MMM yyyy HH:mm', 'yyyy MMM dd HH:mm', +export const dateFormats = ['MMM dd yyyy HH:mm', 'dd MMM yyyy HH:mm', 'dd MMM yyyy HH:mm:ss', 'yyyy MMM dd HH:mm', 'MM/dd/yyyy HH:mm', 'dd/MM/yyyy HH:mm', 'yyyy/MM/dd HH:mm:ss', 'yyyy-MM-dd HH:mm:ss', 'yyyy-MM-dd HH:mm:ss.SSS'] .map(f => simpleDateFormat(f)).concat([lastUpdateAgoDateFormat(), customDateFormat('EEE, MMMM dd, yyyy')]); diff --git a/ui-ngx/src/app/shared/models/widget.models.ts b/ui-ngx/src/app/shared/models/widget.models.ts index 5f4ca8f7be..c736ed7404 100644 --- a/ui-ngx/src/app/shared/models/widget.models.ts +++ b/ui-ngx/src/app/shared/models/widget.models.ts @@ -38,11 +38,12 @@ import { AbstractControl, UntypedFormGroup } from '@angular/forms'; import { Observable } from 'rxjs'; import { Dashboard } from '@shared/models/dashboard.models'; import { IAliasController } from '@core/api/widget-api.models'; -import { isNotEmptyStr } from '@core/utils'; +import { isNotEmptyStr, mergeDeep } from '@core/utils'; import { WidgetConfigComponentData } from '@home/models/widget-component.models'; import { ComponentStyle, Font, TimewindowStyle } from '@shared/models/widget-settings.models'; import { NULL_UUID } from '@shared/models/id/has-uuid'; import { HasTenantId } from '@shared/models/entity.models'; +import { DataKeysCallbacks, DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models'; export enum widgetType { timeseries = 'timeseries', @@ -184,6 +185,7 @@ export interface WidgetTypeParameters { hideDataSettings?: boolean; defaultDataKeysFunction?: (configComponent: any, configData: any) => DataKey[]; defaultLatestDataKeysFunction?: (configComponent: any, configData: any) => DataKey[]; + dataKeySettingsFunction?: DataKeySettingsFunction; displayRpcMessageToast?: boolean; } @@ -553,6 +555,8 @@ export enum WidgetMobileActionType { takeScreenshot = 'takeScreenshot' } +export const widgetActionTypes = Object.keys(WidgetActionType) as WidgetActionType[]; + export const widgetActionTypeTranslationMap = new Map( [ [ WidgetActionType.openDashboardState, 'widget-action.open-dashboard-state' ], @@ -813,6 +817,7 @@ export interface WidgetSize { export interface IWidgetSettingsComponent { aliasController: IAliasController; + dataKeyCallbacks: DataKeysCallbacks; dashboard: Dashboard; widget: Widget; widgetConfig: WidgetConfigComponentData; @@ -830,6 +835,8 @@ export abstract class WidgetSettingsComponent extends PageComponent implements aliasController: IAliasController; + dataKeyCallbacks: DataKeysCallbacks; + dashboard: Dashboard; widget: Widget; @@ -855,7 +862,7 @@ export abstract class WidgetSettingsComponent extends PageComponent implements if (!value) { this.settingsValue = this.defaultSettings(); } else { - this.settingsValue = {...this.defaultSettings(), ...value}; + this.settingsValue = mergeDeep(this.defaultSettings(), value); } if (!this.settingsSet) { this.settingsSet = true; diff --git a/ui-ngx/src/assets/dashboard/api_usage.json b/ui-ngx/src/assets/dashboard/api_usage.json index 8880c1ebe4..c0f3645156 100644 --- a/ui-ngx/src/assets/dashboard/api_usage.json +++ b/ui-ngx/src/assets/dashboard/api_usage.json @@ -1,5007 +1,8725 @@ { - "title": "Api Usage", - "image": null, - "mobileHide": false, - "mobileOrder": null, - "configuration": { - "description": "", - "widgets": { - "fd6df872-2ddf-0921-3929-2e7f55062fad": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "jsExecutionApiState", - "type": "timeseries", - "label": "jsApiState", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8830669138660703, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value : 'ENABLED';", - "aggregationType": "NONE" - }, - { - "name": "jsExecutionLimit", - "type": "timeseries", - "label": "jsLimit", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5463603803546802, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null, - "aggregationType": "NONE" - }, - { - "name": "jsExecutionCount", - "type": "timeseries", - "label": "jsCount", - "color": "#f44336", - "settings": {}, - "_hash": 0.5564241862015964, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null, - "aggregationType": "NONE" + "title": "Api Usage", + "image": null, + "mobileHide": false, + "mobileOrder": null, + "configuration": { + "description": "", + "widgets": { + "a669cf86-e715-efa4-dd9a-b839abf499e9": { + "type": "timeseries", + "sizeX": 24, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "dataKeys": [ + { + "name": "ruleEngineException", + "type": "timeseries", + "label": "Rule Chain", + "color": "#2196f3", + "settings": { + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "return JSON.parse(value).ruleChainName;" + }, + "_hash": 0.9954481282345906 + }, + { + "name": "ruleEngineException", + "type": "timeseries", + "label": "Rule Node", + "color": "#4caf50", + "settings": { + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "return JSON.parse(value).ruleNodeName;" + }, + "_hash": 0.18580357036589978 + }, + { + "name": "ruleEngineException", + "type": "timeseries", + "label": "Latest Error", + "color": "#f44336", + "settings": { + "useCellStyleFunction": false, + "useCellContentFunction": true, + "cellContentFunction": "return JSON.parse(value).message;" + }, + "_hash": 0.7255162989552142 + } + ], + "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f" + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 0, + "realtime": { + "timewindowMs": 2592000000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "settings": { + "showTimestamp": true, + "displayPagination": true, + "defaultPageSize": 10 + }, + "title": "{i18n:api-usage.exceptions}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "useDashboardTimewindow": false, + "showLegend": false, + "widgetStyle": {}, + "actions": {}, + "showTitleIcon": false, + "titleIcon": null, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "displayTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "configMode": "basic" }, - { - "name": "jsExecutionApiState", - "type": "timeseries", - "label": "cardId", - "color": "#607d8b", - "settings": {}, - "_hash": 0.051659774305067296, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return (Math.random()*100000).toFixed(0);" + "id": "a669cf86-e715-efa4-dd9a-b839abf499e9", + "typeFullFqn": "system.cards.timeseries_table" + }, + "aab68ab5-8e40-8694-c55c-8eb1c89b88fb": { + "typeFullFqn": "system.cards.markdown_card", + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "transportMsgLimit", + "type": "timeseries", + "label": "limit", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5463603803546802, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", + "aggregationType": "NONE" + }, + { + "name": "transportMsgCount", + "type": "timeseries", + "label": "count", + "color": "#f44336", + "settings": {}, + "_hash": 0.5564241862015964, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;\n", + "aggregationType": "NONE" + }, + { + "name": "transportDataPointsLimit", + "type": "timeseries", + "label": "pointsLimit", + "color": "#9c27b0", + "settings": {}, + "_hash": 0.22082255831864894, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", + "aggregationType": "NONE" + }, + { + "name": "transportDataPointsCount", + "type": "timeseries", + "label": "pointsCount", + "color": "#8bc34a", + "settings": {}, + "_hash": 0.6340356364819146, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", + "aggregationType": "NONE" + }, + { + "name": "transportApiState", + "type": "timeseries", + "label": "title", + "color": "#3f51b5", + "settings": {}, + "_hash": 0.6894070537030252, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.transport}\";" + }, + { + "name": "transportApiState", + "type": "timeseries", + "label": "apiStatus", + "color": "#3f51b5", + "settings": {}, + "_hash": 0.430957831457494, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value ? value.toLowerCase() : 'enabled';" + }, + { + "name": "transportApiState", + "type": "timeseries", + "label": "unit", + "color": "#8bc34a", + "settings": {}, + "_hash": 0.662147926074595, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return '{i18n:api-usage.messages}';" + }, + { + "name": "transportApiState", + "type": "timeseries", + "label": "pointsUnit", + "color": "#3f51b5", + "settings": {}, + "_hash": 0.44620898738917947, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return '{i18n:api-usage.data-points}';" + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1708518962586, + "endTimeMs": 1708605362586 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "useMarkdownTextFunction": true, + "markdownTextPattern": "", + "markdownTextFunction": "function toShortNumber(number) {\n const rounder = Math.pow(10, 1);\n const powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n ];\n let key = '';\n for (const power of powers) {\n const reduced = number / power.value;\n for (const power of powers) {\n let reduced = number / power.value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n number = reduced;\n key = power.key;\n break;\n }\n }\n }\n \n return number + key;\n}\n\nfunction calculateBarValues(count, limit) {\n let apiUsageBar = '0%';\n let apiUsagePercent = '';\n let apiUsageValue = `${toShortNumber(count)} / ∞`;\n if (Number.isFinite(limit) && limit > 0) {\n var percent = Math.min(100, ((count / limit) * 100));\n apiUsageBar = `${percent}%`\n apiUsagePercent = `${percent.toFixed(2)}%`;\n apiUsageValue = `${toShortNumber(count)} / ${toShortNumber(limit)}`;\n }\n \n return [apiUsageBar, apiUsagePercent, apiUsageValue]\n}\n\nconst [apiUsageBar, apiUsagePercent, apiUsageValue] = calculateBarValues(data[0].count, data[0].limit);\nconst [apiUsageBar2, apiUsagePercent2, apiUsageValue2] = calculateBarValues(data[0].pointsCount, data[0].pointsLimit);\n\n\nreturn `
` +\n '
' +\n '
' +\n '
' +\n '
' +\n `${data[0].title}` +\n '
' +\n `
${data[0].apiStatus.toUpperCase()}
` +\n '
' +\n '
' +\n '
' +\n '
' +\n `
${data[0].unit}
` +\n '
' +\n `
` +\n '
' +\n '
' +\n `
${apiUsagePercent}
` +\n '
' +\n `
${apiUsageValue}
` +\n '
' +\n '
' +\n '
' +\n '
' +\n '
' +\n `
${data[0].pointsUnit}
` +\n '
' +\n `
` +\n '
' +\n '
' +\n `
${apiUsagePercent2}
` +\n '
' +\n `
${apiUsageValue2}
` +\n '
' +\n '
' + \n '
' +\n '
' +\n '
' +\n '
' +\n '' +\n '
'+\n '' +\n '
' +\n '
'\n", + "applyDefaultMarkdownStyle": false, + "markdownCss": "\n" + }, + "title": "Transport", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "displayTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "actions": { + "elementClick": [ + { + "name": "transport_details", + "icon": "insert_chart", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "openDashboardState", + "targetDashboardStateId": "transport", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "openInSeparateDialog": false, + "openInPopover": false, + "id": "a60e09be-1bea-dfc3-6abb-f87e73256899" + } + ] + } }, - { - "name": "jsExecutionApiState", - "type": "timeseries", - "label": "title", - "color": "#9c27b0", - "settings": {}, - "_hash": 0.7673280949238444, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.scripts}\";", - "aggregationType": "NONE" + "row": 0, + "col": 0, + "id": "aab68ab5-8e40-8694-c55c-8eb1c89b88fb" + }, + "a84fa70a-ddfa-3b24-9aa4-cf9ce91f919a": { + "typeFullFqn": "system.cards.markdown_card", + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "ruleEngineApiState", + "type": "timeseries", + "label": "apiStatus", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8830669138660703, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value ? value : 'enabled';", + "aggregationType": "NONE" + }, + { + "name": "ruleEngineExecutionLimit", + "type": "timeseries", + "label": "limit", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5463603803546802, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", + "aggregationType": "NONE" + }, + { + "name": "ruleEngineExecutionCount", + "type": "timeseries", + "label": "count", + "color": "#f44336", + "settings": {}, + "_hash": 0.5564241862015964, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", + "aggregationType": "NONE" + }, + { + "name": "ruleEngineApiState", + "type": "timeseries", + "label": "title", + "color": "#9c27b0", + "settings": {}, + "_hash": 0.3551317421302518, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.rule-engine}\";" + }, + { + "name": "ruleEngineApiState", + "type": "timeseries", + "label": "unit", + "color": "#8bc34a", + "settings": {}, + "_hash": 0.5100381746798048, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.executions}\";" + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1708518962586, + "endTimeMs": 1708605362586 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "useMarkdownTextFunction": true, + "markdownTextPattern": "", + "markdownTextFunction": "function toShortNumber(number) {\n const rounder = Math.pow(10, 1);\n const powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n ];\n let key = '';\n for (const power of powers) {\n let reduced = number / power.value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n number = reduced;\n key = power.key;\n break;\n }\n }\n \n return number + key;\n}\n\nfunction calculateBarValues(count, limit) {\n let apiUsageBar = '0%';\n let apiUsagePercent = '';\n let apiUsageValue = `${toShortNumber(count)} / ∞`;\n if (Number.isFinite(limit) && limit > 0) {\n var percent = Math.min(100, ((count / limit) * 100));\n apiUsageBar = `${percent}%`\n apiUsagePercent = `${percent.toFixed(2)}%`;\n apiUsageValue = `${toShortNumber(count)} / ${toShortNumber(limit)}`;\n }\n \n return [apiUsageBar, apiUsagePercent, apiUsageValue]\n}\n\nconst [apiUsageBar, apiUsagePercent, apiUsageValue] = calculateBarValues(data[0].count, data[0].limit);\n\n\nreturn `
` +\n '
' +\n '
' +\n '
' +\n '
${title}
' +\n `
${data[0].apiStatus.toUpperCase()}
` +\n '
' +\n '
' +\n `
${data[0].unit}
` +\n '
' +\n `
` +\n '
' +\n '
' +\n `
${apiUsagePercent}
` +\n '
' +\n `
${apiUsageValue}
` +\n '
' +\n '
' +\n '
' +\n '
' +\n '' +\n '
'+\n '' +\n '' +\n '
' +\n '
'\n", + "applyDefaultMarkdownStyle": false, + "markdownCss": "\n" + }, + "title": "Rule Engine execution", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "displayTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "actions": { + "elementClick": [ + { + "name": "rule_engine_execution_details", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "rule_engine_execution", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "id": "3c30248f-0cd8-fb97-a917-bc1e09984a79" + }, + { + "name": "rule_engine_statistics_details", + "icon": "show_chart", + "type": "openDashboardState", + "targetDashboardStateId": "rule_engine_statistics", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "id": "04e4565a-9e24-23df-f376-f2ec70a8165f" + } + ] + } }, - { - "name": "jsExecutionApiState", - "type": "timeseries", - "label": "jsUnit", - "color": "#8bc34a", - "settings": {}, - "_hash": 0.7926918686567068, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.javascript}\";", - "aggregationType": "NONE" + "row": 0, + "col": 0, + "id": "a84fa70a-ddfa-3b24-9aa4-cf9ce91f919a" + }, + "d70d26d4-e22d-4ca9-9ea7-f9c87c093321": { + "typeFullFqn": "system.cards.markdown_card", + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "jsExecutionApiState", + "type": "timeseries", + "label": "jsApiState", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8830669138660703, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value ? value : 'ENABLED';", + "aggregationType": "NONE" + }, + { + "name": "jsExecutionLimit", + "type": "timeseries", + "label": "jsLimit", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5463603803546802, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", + "aggregationType": "NONE" + }, + { + "name": "jsExecutionCount", + "type": "timeseries", + "label": "jsCount", + "color": "#f44336", + "settings": {}, + "_hash": 0.5564241862015964, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", + "aggregationType": "NONE" + }, + { + "name": "jsExecutionApiState", + "type": "timeseries", + "label": "title", + "color": "#9c27b0", + "settings": {}, + "_hash": 0.7673280949238444, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.scripts}\";", + "aggregationType": "NONE" + }, + { + "name": "jsExecutionApiState", + "type": "timeseries", + "label": "jsUnit", + "color": "#8bc34a", + "settings": {}, + "_hash": 0.7926918686567068, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.javascript}\";", + "aggregationType": "NONE" + }, + { + "name": "tbelExecutionApiState", + "type": "timeseries", + "label": "tbelApiState", + "color": "#3f51b5", + "settings": {}, + "_hash": 0.2002981454581909, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value ? value : 'ENABLED';" + }, + { + "name": "tbelExecutionLimit", + "type": "timeseries", + "label": "tbelLimit", + "color": "#ffeb3b", + "settings": {}, + "_hash": 0.5039854873031677, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;" + }, + { + "name": "tbelExecutionCount", + "type": "timeseries", + "label": "tbelCount", + "color": "#e91e63", + "settings": {}, + "_hash": 0.9506731992087107, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;" + }, + { + "name": "tbelExecutionApiState", + "type": "timeseries", + "label": "tbelUnit", + "color": "#ffeb3b", + "settings": {}, + "_hash": 0.3673530683177082, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.tbel}\";" + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1708518962586, + "endTimeMs": 1708605362586 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "useMarkdownTextFunction": true, + "markdownTextPattern": "", + "markdownTextFunction": "function toShortNumber(number) {\n const rounder = Math.pow(10, 1);\n const powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n ];\n let key = '';\n for (const power of powers) {\n const reduced = number / power.value;\n for (const power of powers) {\n let reduced = number / power.value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n number = reduced;\n key = power.key;\n break;\n }\n }\n }\n \n return number + key;\n}\n\nfunction calculateBarValues(count, limit) {\n let apiUsageBar = '0%';\n let apiUsagePercent = '';\n let apiUsageValue = `${toShortNumber(count)} / ∞`;\n if (Number.isFinite(limit) && limit > 0) {\n var percent = Math.min(100, ((count / limit) * 100));\n apiUsageBar = `${percent}%`\n apiUsagePercent = `${percent.toFixed(2)}%`;\n apiUsageValue = `${toShortNumber(count)} / ${toShortNumber(limit)}`;\n }\n \n return [apiUsageBar, apiUsagePercent, apiUsageValue]\n}\n\nconst [jsUsageBar, jsUsagePercent, jsUsageValue] = calculateBarValues(data[0].jsCount, data[0].jsLimit);\nconst [tbelUsageBar, tbelUsagePercent, tbelUsageValue] = calculateBarValues(data[0].tbelCount, data[0].tbelLimit);\n\nconst jsApiState = data[0].jsApiState;\nconst tbelApiState = data[0].tbelApiState;\nlet currentState;\nif (jsApiState === 'DISABLED' || tbelApiState === 'DISABLED') {\n currentState = 'DISABLED';\n} else if (jsApiState === 'WARNING' || tbelApiState === 'WARNING') {\n currentState = 'WARNING';\n} else {\n currentState = 'ENABLED';\n}\nconst cardClass = currentState.toLowerCase()\n\nreturn `
` +\n '
' +\n '
' +\n '
' +\n '
' +\n `${data[0].title}` +\n '
' +\n `
${currentState}
` +\n '
' +\n '
' +\n '
' +\n '
' +\n `
${data[0].jsUnit}
` +\n '
' +\n `
` +\n '
' +\n '
' +\n `
${jsUsagePercent}
` +\n '
' +\n `
${jsUsageValue}
` +\n '
' +\n '
' +\n '
' +\n '
' +\n '
' +\n `
${data[0].tbelUnit}
` +\n '
' +\n `
` +\n '
' +\n '
' +\n `
${tbelUsagePercent}
` +\n '
' +\n `
${tbelUsageValue}
` +\n '
' +\n '
' + \n '
' +\n '
' +\n '
' +\n '
' +\n '' +\n '
'+\n '' +\n '
' +\n '
'\n", + "applyDefaultMarkdownStyle": false, + "markdownCss": "\n" + }, + "title": "JavaScript functions", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "displayTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "actions": { + "elementClick": [ + { + "name": "script_functions_details", + "icon": "insert_chart", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "openDashboardState", + "targetDashboardStateId": "script_functions", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "openInSeparateDialog": false, + "openInPopover": false, + "id": "d4961bea-84de-e1af-e50f-666b98d34cd5" + } + ] + } }, - { - "name": "tbelExecutionApiState", - "type": "timeseries", - "label": "tbelApiState", - "color": "#3f51b5", - "settings": {}, - "_hash": 0.2002981454581909, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value : 'ENABLED';" + "row": 0, + "col": 0, + "id": "d70d26d4-e22d-4ca9-9ea7-f9c87c093321" + }, + "4d3ea95c-3188-9872-1817-2f989c7729e0": { + "typeFullFqn": "system.cards.markdown_card", + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "storageDataPointsLimit", + "type": "timeseries", + "label": "limit", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5463603803546802, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", + "aggregationType": "NONE" + }, + { + "name": "storageDataPointsCount", + "type": "timeseries", + "label": "count", + "color": "#f44336", + "settings": {}, + "_hash": 0.5564241862015964, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", + "aggregationType": "NONE" + }, + { + "name": "dbApiState", + "type": "timeseries", + "label": "apiStatus", + "color": "#ffc107", + "settings": {}, + "_hash": 0.8737107059960671, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value ? value : 'enabled';", + "aggregationType": "NONE" + }, + { + "name": "dbApiState", + "type": "timeseries", + "label": "title", + "color": "#9c27b0", + "settings": {}, + "_hash": 0.6301889725474652, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.telemetry}\";" + }, + { + "name": "dbApiState", + "type": "timeseries", + "label": "unit", + "color": "#8bc34a", + "settings": {}, + "_hash": 0.0027742924142306613, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.data-points-storage-days}\";" + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1708518962586, + "endTimeMs": 1708605362586 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "useMarkdownTextFunction": true, + "markdownTextPattern": "", + "markdownTextFunction": "function toShortNumber(number) {\n const rounder = Math.pow(10, 1);\n const powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n ];\n let key = '';\n for (const power of powers) {\n let reduced = number / power.value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n number = reduced;\n key = power.key;\n break;\n }\n }\n \n return number + key;\n}\n\nfunction calculateBarValues(count, limit) {\n let apiUsageBar = '0%';\n let apiUsagePercent = '';\n let apiUsageValue = `${toShortNumber(count)} / ∞`;\n if (Number.isFinite(limit) && limit > 0) {\n var percent = Math.min(100, ((count / limit) * 100));\n apiUsageBar = `${percent}%`\n apiUsagePercent = `${percent.toFixed(2)}%`;\n apiUsageValue = `${toShortNumber(count)} / ${toShortNumber(limit)}`;\n }\n \n return [apiUsageBar, apiUsagePercent, apiUsageValue]\n}\n\nconst [apiUsageBar, apiUsagePercent, apiUsageValue] = calculateBarValues(data[0].count, data[0].limit);\n\n\nreturn `
` +\n '
' +\n '
' +\n '
' +\n '
${title}
' +\n `
${data[0].apiStatus.toUpperCase()}
` +\n '
' +\n '
' +\n `
${data[0].unit}
` +\n '
' +\n `
` +\n '
' +\n '
' +\n `
${apiUsagePercent}
` +\n '
' +\n `
${apiUsageValue}
` +\n '
' +\n '
' +\n '
' +\n '
' +\n '' +\n '
'+\n '' +\n '
' +\n '
'\n", + "applyDefaultMarkdownStyle": false, + "markdownCss": "\n" + }, + "title": "Telemetry persistence", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "displayTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "actions": { + "elementClick": [ + { + "name": "telemetry_persistence_details", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "telemetry_persistence", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "id": "6248831c-5b3f-8879-8548-afcf43f10610" + } + ] + } }, - { - "name": "tbelExecutionLimit", - "type": "timeseries", - "label": "tbelLimit", - "color": "#ffeb3b", - "settings": {}, - "_hash": 0.5039854873031677, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null + "row": 0, + "col": 0, + "id": "4d3ea95c-3188-9872-1817-2f989c7729e0" + }, + "2d0d6ff6-cd59-51d4-b916-38e22cdd0702": { + "typeFullFqn": "system.cards.markdown_card", + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "createdAlarmsLimit", + "type": "timeseries", + "label": "limit", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5463603803546802, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", + "aggregationType": "NONE" + }, + { + "name": "createdAlarmsCount", + "type": "timeseries", + "label": "count", + "color": "#f44336", + "settings": {}, + "_hash": 0.5564241862015964, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", + "aggregationType": "NONE" + }, + { + "name": "alarmApiState", + "type": "timeseries", + "label": "apiStatus", + "color": "#ffc107", + "settings": {}, + "_hash": 0.8737107059960671, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value ? value : 'enabled';", + "aggregationType": "NONE" + }, + { + "name": "alarmApiState", + "type": "timeseries", + "label": "title", + "color": "#9c27b0", + "settings": {}, + "_hash": 0.43439375716502227, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.alarm}\";" + }, + { + "name": "alarmApiState", + "type": "timeseries", + "label": "unit", + "color": "#8bc34a", + "settings": {}, + "_hash": 0.9964061963495883, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.alarms-created}\";" + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1708518962586, + "endTimeMs": 1708605362586 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "useMarkdownTextFunction": true, + "markdownTextPattern": "", + "markdownTextFunction": "function toShortNumber(number) {\n const rounder = Math.pow(10, 1);\n const powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n ];\n let key = '';\n for (const power of powers) {\n let reduced = number / power.value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n number = reduced;\n key = power.key;\n break;\n }\n }\n \n return number + key;\n}\n\nfunction calculateBarValues(count, limit) {\n let apiUsageBar = '0%';\n let apiUsagePercent = '';\n let apiUsageValue = `${toShortNumber(count)} / ∞`;\n if (Number.isFinite(limit) && limit > 0) {\n var percent = Math.min(100, ((count / limit) * 100));\n apiUsageBar = `${percent}%`\n apiUsagePercent = `${percent.toFixed(2)}%`;\n apiUsageValue = `${toShortNumber(count)} / ${toShortNumber(limit)}`;\n }\n \n return [apiUsageBar, apiUsagePercent, apiUsageValue]\n}\n\nconst [apiUsageBar, apiUsagePercent, apiUsageValue] = calculateBarValues(data[0].count, data[0].limit);\n\n\nreturn `
` +\n '
' +\n '
' +\n '
' +\n '
${title}
' +\n `
${data[0].apiStatus.toUpperCase()}
` +\n '
' +\n '
' +\n `
${data[0].unit}
` +\n '
' +\n `
` +\n '
' +\n '
' +\n `
${apiUsagePercent}
` +\n '
' +\n `
${apiUsageValue}
` +\n '
' +\n '
' +\n '
' +\n '
' +\n '' +\n '
'+\n '' +\n '
' +\n '
'\n", + "applyDefaultMarkdownStyle": false, + "markdownCss": "\n" + }, + "title": "Alarm created", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "displayTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "actions": { + "elementClick": [ + { + "name": "email_messages_details", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "alarms_created", + "setEntityId": false, + "stateEntityParamName": null, + "openInSeparateDialog": null, + "dialogTitle": null, + "dialogHideDashboardToolbar": true, + "dialogWidth": null, + "dialogHeight": null, + "openRightLayout": false, + "id": "946ba769-84ac-1507-6baa-94701de8967b" + } + ] + } }, - { - "name": "tbelExecutionCount", - "type": "timeseries", - "label": "tbelCount", - "color": "#e91e63", - "settings": {}, - "_hash": 0.9506731992087107, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null + "row": 0, + "col": 0, + "id": "2d0d6ff6-cd59-51d4-b916-38e22cdd0702" + }, + "120573cc-e246-eb49-7d80-68e5d3b3c0cc": { + "typeFullFqn": "system.cards.markdown_card", + "type": "latest", + "sizeX": 5, + "sizeY": 3.5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "emailApiState", + "type": "timeseries", + "label": "apiState", + "color": "#2196f3", + "settings": {}, + "_hash": 0.8830669138660703, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "emailLimit", + "type": "timeseries", + "label": "limit", + "color": "#4caf50", + "settings": {}, + "_hash": 0.5463603803546802, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", + "aggregationType": "NONE" + }, + { + "name": "emailCount", + "type": "timeseries", + "label": "count", + "color": "#f44336", + "settings": {}, + "_hash": 0.5564241862015964, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", + "aggregationType": "NONE" + }, + { + "name": "smsApiState", + "type": "timeseries", + "label": "apiStatePoint", + "color": "#e91e63", + "settings": {}, + "_hash": 0.2969682764607864, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "smsLimit", + "type": "timeseries", + "label": "pointsLimit", + "color": "#9c27b0", + "settings": {}, + "_hash": 0.22082255831864894, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", + "aggregationType": "NONE" + }, + { + "name": "smsCount", + "type": "timeseries", + "label": "pointsCount", + "color": "#8bc34a", + "settings": {}, + "_hash": 0.6340356364819146, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return value !== '' ? parseInt(value, 10) : 0;", + "aggregationType": "NONE" + }, + { + "name": "notificationApiState", + "type": "timeseries", + "label": "title", + "color": "#3f51b5", + "settings": {}, + "_hash": 0.6894070537030252, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return \"{i18n:api-usage.notifications}\";", + "aggregationType": "NONE" + }, + { + "name": "notificationApiState", + "type": "timeseries", + "label": "unit", + "color": "#3f51b5", + "settings": {}, + "_hash": 0.0005447336528170421, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return '{i18n:api-usage.email}';" + }, + { + "name": "notificationApiState", + "type": "timeseries", + "label": "pointsUnit", + "color": "#e91e63", + "settings": {}, + "_hash": 0.12117146988088967, + "aggregationType": "NONE", + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": true, + "postFuncBody": "return '{i18n:api-usage.sms}';" + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "displayValue": "", + "selectedTab": 0, + "realtime": { + "realtimeType": 1, + "interval": 1000, + "timewindowMs": 60000, + "quickInterval": "CURRENT_DAY" + }, + "history": { + "historyType": 0, + "interval": 1000, + "timewindowMs": 60000, + "fixedTimewindow": { + "startTimeMs": 1708518962586, + "endTimeMs": 1708605362586 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + } + }, + "showTitle": false, + "backgroundColor": "#fff", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "0px", + "settings": { + "useMarkdownTextFunction": true, + "markdownTextPattern": "", + "markdownTextFunction": "function toShortNumber(number) {\n const rounder = Math.pow(10, 1);\n const powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n ];\n let key = '';\n for (const power of powers) {\n const reduced = number / power.value;\n for (const power of powers) {\n let reduced = number / power.value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n number = reduced;\n key = power.key;\n break;\n }\n }\n }\n \n return number + key;\n}\n\nfunction calculateBarValues(count, limit) {\n let apiUsageBar = '0%';\n let apiUsagePercent = '';\n let apiUsageValue = `${toShortNumber(count)} / ∞`;\n if (Number.isFinite(limit) && limit > 0) {\n var percent = Math.min(100, ((count / limit) * 100));\n apiUsageBar = `${percent}%`\n apiUsagePercent = `${percent.toFixed(2)}%`;\n apiUsageValue = `${toShortNumber(count)} / ${toShortNumber(limit)}`;\n }\n \n return [apiUsageBar, apiUsagePercent, apiUsageValue]\n}\n\nconst [apiUsageBar, apiUsagePercent, apiUsageValue] = calculateBarValues(data[0].count, data[0].limit);\nconst [apiUsageBar2, apiUsagePercent2, apiUsageValue2] = calculateBarValues(data[0].pointsCount, data[0].pointsLimit);\n\nconst apiState = data[0].apiState;\nconst apiStatePoint = data[0].apiStatePoint;\nlet currentState;\nif (apiState === 'DISABLED' || apiStatePoint === 'DISABLED') {\n currentState = 'DISABLED';\n} else if (apiState === 'WARNING' || apiStatePoint === 'WARNING') {\n currentState = 'WARNING';\n} else {\n currentState = 'ENABLED';\n}\n\n\n\nreturn `
` +\n '
' +\n '
' +\n '
' +\n '
' +\n `${data[0].title}` +\n '
' +\n `
${currentState}
` +\n '
' +\n '
' +\n '
' +\n '
' +\n `
${data[0].unit}
` +\n '
' +\n `
` +\n '
' +\n '
' +\n `
${apiUsagePercent}
` +\n '
' +\n `
${apiUsageValue}
` +\n '
' +\n '
' +\n '
' +\n '
' +\n '
' +\n `
${data[0].pointsUnit}
` +\n '
' +\n `
` +\n '
' +\n '
' +\n `
${apiUsagePercent2}
` +\n '
' +\n `
${apiUsageValue2}
` +\n '
' +\n '
' + \n '
' +\n '
' +\n '
' +\n '
' +\n '' +\n '
'+\n '' +\n '
' +\n '
'\n", + "applyDefaultMarkdownStyle": false, + "markdownCss": "\n" + }, + "title": "Notifications (Email/SMS)", + "showTitleIcon": false, + "iconColor": "rgba(0, 0, 0, 0.87)", + "iconSize": "24px", + "titleTooltip": "", + "dropShadow": true, + "enableFullscreen": false, + "widgetStyle": {}, + "titleStyle": { + "fontSize": "16px", + "fontWeight": 400 + }, + "showLegend": false, + "useDashboardTimewindow": true, + "displayTimewindow": true, + "widgetCss": "", + "pageSize": 1024, + "noDataDisplayMessage": "", + "actions": { + "elementClick": [ + { + "name": "transport_details", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "notifications", + "setEntityId": false, + "stateEntityParamName": null, + "openInSeparateDialog": null, + "dialogTitle": null, + "dialogHideDashboardToolbar": true, + "dialogWidth": null, + "dialogHeight": null, + "openRightLayout": false, + "id": "46b7cefe-e1f2-67c1-4055-3a214520f869" + } + ] + } }, - { - "name": "tbelExecutionApiState", - "type": "timeseries", - "label": "tbelUnit", - "color": "#ffeb3b", - "settings": {}, - "_hash": 0.3673530683177082, - "aggregationType": "NONE", - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.tbel}\";" - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1694083375825, - "endTimeMs": 1694169775825 - }, - "quickInterval": "CURRENT_DAY" + "row": 0, + "col": 0, + "id": "120573cc-e246-eb49-7d80-68e5d3b3c0cc" }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "#666666", - "padding": "0", - "settings": { - "cardHtml": "
\n \n \n
\n
\n
\n
${title}
\n
\n
\n
\n
\n
\n
${jsUnit}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
${tbelUnit}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
", - "cardCss": ".card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 13px 13px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bars-row {\n flex: 1;\n display: flex;\n flex-direction: row;\n}\n\n.card .bar-column {\n flex: 1;\n display: flex;\n flex-direction: column;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.card .action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 6px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} " - }, - "title": "JavaScript functions", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": { - "cursor": "default" - }, - "titleStyle": { - "fontSize": "20px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "showLegend": false, - "actions": { - "elementClick": [ - { - "name": "script_functions_details", - "icon": "insert_chart", - "useShowWidgetActionFunction": null, - "showWidgetActionFunction": "return true;", - "type": "openDashboardState", - "targetDashboardStateId": "script_functions", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "openInSeparateDialog": false, - "openInPopover": false, - "id": "d4961bea-84de-e1af-e50f-666b98d34cd5" - } - ] - }, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "displayTimewindow": true - }, - "row": 0, - "col": 0, - "id": "fd6df872-2ddf-0921-3929-2e7f55062fad", - "typeFullFqn": "system.cards.html_value_card" - }, - "7e235874-461b-e7c2-2fdd-d8762a020773": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "dbApiState", - "type": "timeseries", - "label": "apiState", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8830669138660703, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value : 'ENABLED';" - }, - { - "name": "storageDataPointsLimit", - "type": "timeseries", - "label": "limit", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5463603803546802, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "storageDataPointsCount", - "type": "timeseries", - "label": "count", - "color": "#f44336", - "settings": {}, - "_hash": 0.5564241862015964, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "dbApiState", - "type": "timeseries", - "label": "apiStateClass", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8737107059960671, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value.toLowerCase() : 'enabled';" - }, - { - "name": "dbApiState", - "type": "timeseries", - "label": "cardId", - "color": "#607d8b", - "settings": {}, - "_hash": 0.051659774305067296, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return (Math.random()*100000).toFixed(0);" - }, - { - "name": "dbApiState", - "type": "timeseries", - "label": "title", - "color": "#9c27b0", - "settings": {}, - "_hash": 0.6301889725474652, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.telemetry}\";" + "63f99d90-23ab-f8c2-3290-1e693ded5a2e": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "transportMsgCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.transport-messages}", + "color": "#2196f3", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + }, + "type": "bar" + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": false, + "postFuncBody": null, + "aggregationType": null + }, + { + "name": "transportDataPointsCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.transport-data-points}", + "color": "#4caf50", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + }, + "type": "bar" + }, + "_hash": 0.46849996721308895, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY", + "interval": 300000 + }, + "aggregation": { + "type": "NONE", + "limit": 50000 + }, + "timezone": null + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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": null, + "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": false, + "relativeWidth": 2, + "absoluteWidth": 3600000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.transport-hourly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": null, + "configMode": "basic", + "actions": { + "headerButton": [ + { + "name": "{i18n:api-usage.view-details}", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "transport", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "id": "6ef12f6a-0266-25cf-6ca5-5dcb772252c6" + } + ] + }, + "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" }, - { - "name": "dbApiState", - "type": "timeseries", - "label": "unit", - "color": "#8bc34a", - "settings": {}, - "_hash": 0.0027742924142306613, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.data-points-storage-days}\";" - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1694083375826, - "endTimeMs": 1694169775826 - }, - "quickInterval": "CURRENT_DAY" + "row": 0, + "col": 0, + "id": "63f99d90-23ab-f8c2-3290-1e693ded5a2e" }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "#666666", - "padding": "0", - "settings": { - "cardHtml": "
\n \n \n
\n
\n
\n
${title}
\n
${apiState}
\n
\n
\n
${unit}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
", - "cardCss": ".card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 13px 13px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.card .action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 10px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} \n\n\n" - }, - "title": "Telemetry persistence", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": { - "cursor": "default" - }, - "titleStyle": { - "fontSize": "20px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "showLegend": false, - "actions": { - "elementClick": [ - { - "name": "telemetry_persistence_details", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "telemetry_persistence", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "id": "6248831c-5b3f-8879-8548-afcf43f10610" - } - ] - }, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "7e235874-461b-e7c2-2fdd-d8762a020773", - "typeFullFqn": "system.cards.html_value_card" - }, - "08545554-a0e8-05c7-66df-6000cfeff8a4": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "ruleEngineApiState", - "type": "timeseries", - "label": "apiState", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8830669138660703, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value : 'ENABLED';" - }, - { - "name": "ruleEngineExecutionLimit", - "type": "timeseries", - "label": "limit", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5463603803546802, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "ruleEngineExecutionCount", - "type": "timeseries", - "label": "count", - "color": "#f44336", - "settings": {}, - "_hash": 0.5564241862015964, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null + "a2b7e906-2d8a-41a8-99a6-409531bfa743": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "ruleEngineExecutionCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.rule-engine-executions}", + "color": "#ab00ff", + "settings": { + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "bar", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "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)", + "pointShape": "emptyCircle", + "pointSize": 4, + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null, + "aggregationType": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_YEAR", + "interval": 7200000 + }, + "aggregation": { + "type": "NONE", + "limit": 50000 + }, + "timezone": null + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": false, + "relativeWidth": 2, + "absoluteWidth": 3600000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.rule-engine-hourly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": null, + "configMode": "basic", + "actions": { + "headerButton": [ + { + "name": "{i18n:api-usage.view-statistics}", + "icon": "show_chart", + "type": "openDashboardState", + "targetDashboardStateId": "rule_engine_statistics", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "id": "f9f08190-9ed9-d802-5b7a-c57ff84b5648" + }, + { + "name": "{i18n:api-usage.view-details}", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "rule_engine_execution", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "id": "1aec196b-44ba-ddf4-c4dc-c3f60c1eb6fc" + } + ] + }, + "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" }, - { - "name": "ruleEngineApiState", - "type": "timeseries", - "label": "apiStateClass", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8737107059960671, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value.toLowerCase() : 'enabled';" + "row": 0, + "col": 0, + "id": "a2b7e906-2d8a-41a8-99a6-409531bfa743" + }, + "ca996b66-ab7e-f977-152c-98e4ebf2a901": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "jsExecutionCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.javascript-executions}", + "color": "#ff9900", + "settings": { + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "bar", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "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)", + "pointShape": "emptyCircle", + "pointSize": 4, + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null, + "aggregationType": null + }, + { + "name": "tbelExecutionCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.tbel-executions}", + "color": "#4caf50", + "settings": { + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "bar", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "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)", + "pointShape": "emptyCircle", + "pointSize": 4, + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.6818645685001823, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY", + "interval": 3600000 + }, + "aggregation": { + "type": "NONE", + "limit": 50000 + }, + "timezone": null + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": false, + "relativeWidth": 2, + "absoluteWidth": 3600000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.scripts-hourly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": null, + "configMode": "basic", + "actions": { + "headerButton": [ + { + "name": "{i18n:api-usage.view-details}", + "icon": "insert_chart", + "useShowWidgetActionFunction": null, + "showWidgetActionFunction": "return true;", + "type": "openDashboardState", + "targetDashboardStateId": "script_functions", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "openInSeparateDialog": false, + "openInPopover": false, + "id": "4687d3f6-8800-a3b6-26e5-0d33f3b828a9" + } + ] + }, + "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" }, - { - "name": "ruleEngineApiState", - "type": "timeseries", - "label": "cardId", - "color": "#607d8b", - "settings": {}, - "_hash": 0.051659774305067296, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return (Math.random()*100000).toFixed(0);" + "row": 0, + "col": 0, + "id": "ca996b66-ab7e-f977-152c-98e4ebf2a901" + }, + "a3c2f1bb-7d3a-f11c-7b3d-28cd84fdfe34": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "storageDataPointsCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.data-points-storage-days}", + "color": "#1039ee", + "settings": { + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "bar", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "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)", + "pointShape": "emptyCircle", + "pointSize": 4, + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null, + "aggregationType": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY", + "interval": 300000 + }, + "aggregation": { + "type": "NONE", + "limit": 50000 + }, + "timezone": null + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": false, + "relativeWidth": 2, + "absoluteWidth": 3600000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.telemetry-persistence-hourly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": null, + "configMode": "basic", + "actions": { + "headerButton": [ + { + "name": "{i18n:api-usage.view-details}", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "telemetry_persistence", + "setEntityId": false, + "stateEntityParamName": null, + "openRightLayout": false, + "id": "16707efb-e572-bd02-c219-55fc1b0f672a" + } + ] + }, + "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" }, - { - "name": "ruleEngineApiState", - "type": "timeseries", - "label": "title", - "color": "#9c27b0", - "settings": {}, - "_hash": 0.3551317421302518, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.rule-engine}\";" + "row": 0, + "col": 0, + "id": "a3c2f1bb-7d3a-f11c-7b3d-28cd84fdfe34" + }, + "5cebd4f1-ff6e-62f9-025c-8e7583c3d66a": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "createdAlarmsCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.alarms-created}", + "color": "#d35a00", + "settings": { + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "bar", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "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)", + "pointShape": "emptyCircle", + "pointSize": 4, + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null, + "aggregationType": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY", + "interval": 300000 + }, + "aggregation": { + "type": "NONE", + "limit": 50000 + }, + "timezone": null + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": false, + "relativeWidth": 2, + "absoluteWidth": 3600000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.alarms-created-hourly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": null, + "configMode": "basic", + "actions": { + "headerButton": [ + { + "name": "{i18n:api-usage.view-details}", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "alarms_created", + "setEntityId": false, + "stateEntityParamName": null, + "openInSeparateDialog": null, + "dialogTitle": null, + "dialogHideDashboardToolbar": true, + "dialogWidth": null, + "dialogHeight": null, + "openRightLayout": false, + "id": "371882f9-ea23-3abc-fca8-9449c5dfdd6b" + } + ] + }, + "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" }, - { - "name": "ruleEngineApiState", - "type": "timeseries", - "label": "unit", - "color": "#8bc34a", - "settings": {}, - "_hash": 0.5100381746798048, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.executions}\";" - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" + "row": 0, + "col": 0, + "id": "5cebd4f1-ff6e-62f9-025c-8e7583c3d66a" }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1694083375826, - "endTimeMs": 1694169775826 - }, - "quickInterval": "CURRENT_DAY" + "bc0c8840-a9b5-5583-de7b-9e9450f5d8fe": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "emailCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.email-messages}", + "color": "#4caf50", + "settings": { + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "bar", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "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)", + "pointShape": "emptyCircle", + "pointSize": 4, + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.1348755140779876, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null, + "aggregationType": null + }, + { + "name": "smsCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.sms-messages}", + "color": "#f36021", + "settings": { + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "bar", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "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)", + "pointShape": "emptyCircle", + "pointSize": 4, + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null, + "aggregationType": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY", + "interval": 300000 + }, + "aggregation": { + "type": "NONE", + "limit": 50000 + }, + "timezone": null + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": false, + "relativeWidth": 2, + "absoluteWidth": 3600000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.notifications-hourly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": null, + "configMode": "basic", + "actions": { + "headerButton": [ + { + "name": "{i18n:api-usage.view-details}", + "icon": "insert_chart", + "type": "openDashboardState", + "targetDashboardStateId": "notifications", + "setEntityId": false, + "stateEntityParamName": null, + "openInSeparateDialog": null, + "dialogTitle": null, + "dialogHideDashboardToolbar": true, + "dialogWidth": null, + "dialogHeight": null, + "openRightLayout": false, + "id": "49aefac0-ec5e-d6f3-f39c-8744759f4b19" + } + ] + }, + "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" + }, + "row": 0, + "col": 0, + "id": "bc0c8840-a9b5-5583-de7b-9e9450f5d8fe" }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "#666666", - "padding": "0", - "settings": { - "cardCss": ".card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 13px 13px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.card .action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 10px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} \n\n\n", - "cardHtml": "
\n \n \n
\n
\n
\n
${title}
\n
${apiState}
\n
\n
\n
${unit}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n \n
\n
" - }, - "title": "Rule Engine execution", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": { - "cursor": "default" - }, - "titleStyle": { - "fontSize": "20px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "showLegend": false, - "actions": { - "elementClick": [ - { - "name": "rule_engine_execution_details", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "rule_engine_execution", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "id": "3c30248f-0cd8-fb97-a917-bc1e09984a79" - }, - { - "name": "rule_engine_statistics_details", - "icon": "show_chart", - "type": "openDashboardState", - "targetDashboardStateId": "rule_engine_statistics", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "id": "04e4565a-9e24-23df-f376-f2ec70a8165f" - } - ] - }, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "08545554-a0e8-05c7-66df-6000cfeff8a4", - "typeFullFqn": "system.cards.html_value_card" - }, - "a245c67e-53ec-d299-fa89-69fe2062ccb2": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "transportApiState", - "type": "timeseries", - "label": "apiState", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8830669138660703, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value : 'ENABLED';" + "0b091dc3-eec3-847e-d0ad-fdf12d474e7a": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "transportMsgCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.transport-messages}", + "color": "#2196f3", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + }, + "type": "bar" + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "transportDataPointsCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.transport-data-points}", + "color": "#4caf50", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + }, + "type": "bar" + }, + "_hash": 0.46849996721308895, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000, + "fixedTimewindow": { + "startTimeMs": 1709729389667, + "endTimeMs": 1709815789667 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "SUM", + "limit": 25000 + }, + "timezone": null + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": false, + "relativeWidth": 2, + "absoluteWidth": 1800000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.transport-daily-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": 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" }, - { - "name": "transportMsgLimit", - "type": "timeseries", - "label": "limit", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5463603803546802, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null + "row": 0, + "col": 0, + "id": "0b091dc3-eec3-847e-d0ad-fdf12d474e7a" + }, + "536d7104-49f8-fde6-5827-61b8419f15ec": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "transportMsgCount", + "type": "timeseries", + "label": "{i18n:api-usage.transport-messages}", + "color": "#2196f3", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + }, + "type": "bar" + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null, + "aggregationType": null + }, + { + "name": "transportDataPointsCount", + "type": "timeseries", + "label": "{i18n:api-usage.transport-data-points}", + "color": "#4caf50", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + }, + "type": "bar" + }, + "_hash": 0.46849996721308895, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null, + "aggregationType": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 31536000000, + "interval": 86400000, + "fixedTimewindow": { + "startTimeMs": 1709729389667, + "endTimeMs": 1709815789667 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + }, + "timezone": null + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": 6, + "absoluteWidth": 900000000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.transport-daily-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": 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" }, - { - "name": "transportMsgCount", - "type": "timeseries", - "label": "count", - "color": "#f44336", - "settings": {}, - "_hash": 0.5564241862015964, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null + "row": 0, + "col": 0, + "id": "536d7104-49f8-fde6-5827-61b8419f15ec" + }, + "c77e417c-ad9d-8e23-3ea1-c75edd653bc0": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "ruleEngineExecutionCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.rule-engine-executions}", + "color": "#ab00ff", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + }, + "type": "bar" + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000, + "fixedTimewindow": { + "startTimeMs": 1709729900300, + "endTimeMs": 1709816300300 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "SUM", + "limit": 25000 + }, + "timezone": null + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": false, + "relativeWidth": 2, + "absoluteWidth": 1800000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.rule-engine-daily-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": 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" }, - { - "name": "transportApiState", - "type": "timeseries", - "label": "apiStateClass", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8737107059960671, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value.toLowerCase() : 'enabled';" + "row": 0, + "col": 0, + "id": "c77e417c-ad9d-8e23-3ea1-c75edd653bc0" + }, + "870904d2-d2e1-a1b9-ce56-b03fd47259b5": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "ruleEngineExecutionCount", + "type": "timeseries", + "label": "{i18n:api-usage.rule-engine-executions}", + "color": "#ab00ff", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + }, + "type": "bar" + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 31536000000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": 6, + "absoluteWidth": 900000000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.rule-engine-monthly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": 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" }, - { - "name": "transportApiState", - "type": "timeseries", - "label": "cardId", - "color": "#607d8b", - "settings": {}, - "_hash": 0.051659774305067296, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return (Math.random()*100000).toFixed(0);" + "row": 0, + "col": 0, + "id": "870904d2-d2e1-a1b9-ce56-b03fd47259b5" + }, + "c66e5060-57fd-11e7-6616-65b82c294ac2": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "jsExecutionCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.javascript-executions}", + "color": "#ff9900", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + }, + "type": "bar" + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "tbelExecutionCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.tbel-executions}", + "color": "#4caf50", + "settings": { + "type": "bar" + }, + "_hash": 0.5212969314724616, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000 + }, + "aggregation": { + "type": "SUM", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": false, + "relativeWidth": 2, + "absoluteWidth": 1800000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.scripts-daily-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": 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" }, - { - "name": "transportDataPointsLimit", - "type": "timeseries", - "label": "pointsLimit", - "color": "#9c27b0", - "settings": {}, - "_hash": 0.22082255831864894, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null + "row": 0, + "col": 0, + "id": "c66e5060-57fd-11e7-6616-65b82c294ac2" + }, + "d0e8603e-5d2e-9287-e2c6-8ccbe9c66806": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "jsExecutionCount", + "type": "timeseries", + "label": "{i18n:api-usage.javascript-executions}", + "color": "#ff9900", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + }, + "type": "bar" + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "tbelExecutionCount", + "type": "timeseries", + "label": "{i18n:api-usage.tbel-executions}", + "color": "#4caf50", + "settings": { + "type": "bar" + }, + "_hash": 0.49748239768082403, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 31536000000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": 6, + "absoluteWidth": 900000000 + }, + "barWidth": { + "relative": false, + "relativeWidth": 2, + "absoluteWidth": 900000000 + } + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.scripts-monthly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": 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" }, - { - "name": "transportDataPointsCount", - "type": "timeseries", - "label": "pointsCount", - "color": "#8bc34a", - "settings": {}, - "_hash": 0.6340356364819146, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null + "row": 0, + "col": 0, + "id": "d0e8603e-5d2e-9287-e2c6-8ccbe9c66806" + }, + "7f4100d2-41be-4954-d353-1d45000dbbbb": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "storageDataPointsCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.data-points-storage-days}", + "color": "#1039ee", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + }, + "type": "bar" + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000 + }, + "aggregation": { + "type": "SUM", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": false, + "relativeWidth": 2, + "absoluteWidth": 1800000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.telemetry-persistence-daily-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": 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" }, - { - "name": "transportApiState", - "type": "timeseries", - "label": "title", - "color": "#3f51b5", - "settings": {}, - "_hash": 0.6894070537030252, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.transport}\";" - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" + "row": 0, + "col": 0, + "id": "7f4100d2-41be-4954-d353-1d45000dbbbb" }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1694083375826, - "endTimeMs": 1694169775826 - }, - "quickInterval": "CURRENT_DAY" + "226ef8c9-8488-3664-21ac-0b6217336202": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "storageDataPointsCount", + "type": "timeseries", + "label": "{i18n:api-usage.data-points-storage-days}", + "color": "#1039ee", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + }, + "type": "bar" + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 31536000000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": 6, + "absoluteWidth": 900000000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.telemetry-persistence-monthly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": 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" + }, + "row": 0, + "col": 0, + "id": "226ef8c9-8488-3664-21ac-0b6217336202" }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "#666666", - "padding": "0", - "settings": { - "cardHtml": "
\n \n \n
\n
\n
\n
\n ${title}\n
\n
${apiState}
\n
\n
\n
\n
\n
{i18n:api-usage.messages}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
{i18n:api-usage.data-points}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
", - "cardCss": ".card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 13px 13px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bars-row {\n flex: 1;\n display: flex;\n flex-direction: row;\n}\n\n.card .bar-column {\n flex: 1;\n display: flex;\n flex-direction: column;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.card .action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 6px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} \n\n" - }, - "title": "Transport", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": { - "cursor": "default" - }, - "titleStyle": { - "fontSize": "20px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "showLegend": false, - "actions": { - "elementClick": [ - { - "name": "transport_details", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "transport", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "id": "46b7cefe-e1f2-67c1-4055-3a214520f869" - } - ] - }, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "a245c67e-53ec-d299-fa89-69fe2062ccb2", - "typeFullFqn": "system.cards.html_value_card" - }, - "a151ae60-0326-6116-d818-9070dda8e9c7": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "alarmApiState", - "type": "timeseries", - "label": "apiState", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8830669138660703, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value : 'ENABLED';" + "bef6c27b-9fe7-ee92-40d9-9696c501a1f9": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "createdAlarmsCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.alarms-created}", + "color": "#d35a00", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + }, + "type": "bar" + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000 + }, + "aggregation": { + "type": "SUM", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": false, + "relativeWidth": 2, + "absoluteWidth": 1800000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.alarms-created-daily-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": 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" }, - { - "name": "createdAlarmsLimit", - "type": "timeseries", - "label": "limit", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5463603803546802, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null + "row": 0, + "col": 0, + "id": "bef6c27b-9fe7-ee92-40d9-9696c501a1f9" + }, + "52305cf8-2258-5745-a0e7-41a171594bb3": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "createdAlarmsCount", + "type": "timeseries", + "label": "{i18n:api-usage.alarms-created}", + "color": "#d35a00", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + }, + "type": "bar" + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 31536000000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": 6, + "absoluteWidth": 900000000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.alarms-created-monthly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": 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" }, - { - "name": "createdAlarmsCount", - "type": "timeseries", - "label": "count", - "color": "#f44336", - "settings": {}, - "_hash": 0.5564241862015964, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null + "row": 0, + "col": 0, + "id": "52305cf8-2258-5745-a0e7-41a171594bb3" + }, + "36fdf999-ca22-9a4c-269d-3f004d792792": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "emailCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.email-messages}", + "color": "#d35a00", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + }, + "type": "bar" + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000 + }, + "aggregation": { + "type": "SUM", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": false, + "relativeWidth": 2, + "absoluteWidth": 1800000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.email-messages-daily-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": 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" }, - { - "name": "alarmApiState", - "type": "timeseries", - "label": "apiStateClass", - "color": "#ffc107", - "settings": {}, - "_hash": 0.8737107059960671, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return value ? value.toLowerCase() : 'enabled';" + "row": 0, + "col": 0, + "id": "36fdf999-ca22-9a4c-269d-3f004d792792" + }, + "9a191755-499d-535e-86c5-061102729c02": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "smsCountHourly", + "type": "timeseries", + "label": "{i18n:api-usage.sms-messages}", + "color": "#f36021", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + }, + "type": "bar" + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000 + }, + "aggregation": { + "type": "SUM", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": false, + "relativeWidth": 2, + "absoluteWidth": 1800000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.sms-messages-daily-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": 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" }, - { - "name": "alarmApiState", - "type": "timeseries", - "label": "cardId", - "color": "#607d8b", - "settings": {}, - "_hash": 0.051659774305067296, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return (Math.random()*100000).toFixed(0);" + "row": 0, + "col": 0, + "id": "9a191755-499d-535e-86c5-061102729c02" + }, + "4b266318-8357-33ef-ca5a-74cbf90e014f": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "emailCount", + "type": "timeseries", + "label": "{i18n:api-usage.email-messages}", + "color": "#d35a00", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + }, + "type": "bar" + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 31536000000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": 6, + "absoluteWidth": 900000000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.email-messages-monthly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": 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" }, - { - "name": "alarmApiState", - "type": "timeseries", - "label": "title", - "color": "#9c27b0", - "settings": {}, - "_hash": 0.43439375716502227, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.alarm}\";" + "row": 0, + "col": 0, + "id": "4b266318-8357-33ef-ca5a-74cbf90e014f" + }, + "5aa33b0b-3bd5-7fe7-ee72-f564c2ca79d8": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": null, + "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", + "filterId": null, + "dataKeys": [ + { + "name": "smsCount", + "type": "timeseries", + "label": "{i18n:api-usage.sms-messages}", + "color": "#f36021", + "settings": { + "excludeFromStacking": false, + "hideDataByDefault": false, + "disableDataHiding": false, + "removeFromLegend": false, + "showLines": false, + "fillLines": false, + "showPoints": false, + "showPointShape": "circle", + "pointShapeFormatter": "", + "showPointsLineWidth": 5, + "showPointsRadius": 3, + "showSeparateAxis": false, + "axisPosition": "left", + "thresholds": [ + { + "thresholdValueSource": "predefinedValue" + } + ], + "comparisonSettings": { + "showValuesForComparison": true + }, + "type": "bar" + }, + "_hash": 0.0661644137210089, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ] + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 31536000000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 1000 + } + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": 6, + "absoluteWidth": 900000000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 1000, + "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 + } + } + }, + "title": "{i18n:api-usage.sms-messages-monthly-activity}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": 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" }, - { - "name": "alarmApiState", - "type": "timeseries", - "label": "unit", - "color": "#8bc34a", - "settings": {}, - "_hash": 0.9964061963495883, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.alarms-created}\";" - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" + "row": 0, + "col": 0, + "id": "5aa33b0b-3bd5-7fe7-ee72-f564c2ca79d8" }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1694083375826, - "endTimeMs": 1694169775826 - }, - "quickInterval": "CURRENT_DAY" + "fa938580-33db-f1b3-fafc-bc3e3784ad57": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f", + "dataKeys": [ + { + "name": "successfulMsgs", + "type": "timeseries", + "label": "{i18n:api-usage.successful}", + "color": "#4caf50", + "settings": { + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": false, + "lineType": "solid", + "lineWidth": 2.5, + "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)", + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.15490750967648736, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "failedMsgs", + "type": "timeseries", + "label": "{i18n:api-usage.permanent-failures}", + "color": "#ef5350", + "settings": { + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": false, + "lineType": "solid", + "lineWidth": 2.5, + "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)", + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.4186621166514697, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "tmpFailed", + "type": "timeseries", + "label": "{i18n:api-usage.processing-failures}", + "color": "#ffc107", + "settings": { + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": false, + "lineType": "solid", + "lineWidth": 2.5, + "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)", + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.49891007198715376, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 0, + "realtime": { + "timewindowMs": 3600000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 10000 + } + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": false, + "relativeWidth": 2, + "absoluteWidth": 1800000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": true, + "showMax": true, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 300, + "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" + }, + "title": "{i18n:api-usage.queue-stats}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": 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" + }, + "row": 0, + "col": 0, + "id": "fa938580-33db-f1b3-fafc-bc3e3784ad57" }, - "aggregation": { - "type": "AVG", - "limit": 25000 + "2ee89893-4e38-5331-95b7-3fd4f310c5a7": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f", + "dataKeys": [ + { + "name": "timeoutMsgs", + "type": "timeseries", + "label": "{i18n:api-usage.permanent-timeouts}", + "color": "#4caf50", + "settings": { + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": false, + "lineType": "solid", + "lineWidth": 2.5, + "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)", + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.565222981550328, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "tmpTimeout", + "type": "timeseries", + "label": "{i18n:api-usage.processing-timeouts}", + "color": "#9c27b0", + "settings": { + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": false, + "lineType": "solid", + "lineWidth": 2.5, + "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)", + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.2679547062508352, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + } + } + ], + "timewindow": { + "hideInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "selectedTab": 0, + "realtime": { + "timewindowMs": 3600000, + "interval": 1000 + }, + "aggregation": { + "type": "NONE", + "limit": 10000 + } + }, + "showTitle": true, + "backgroundColor": "#FFFFFF", + "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": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", + "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, + "min": null, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "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": false, + "relativeWidth": 2, + "absoluteWidth": 1800000 + }, + "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": "bottom", + "sortDataKeys": false, + "showMin": true, + "showMax": true, + "showAvg": false, + "showTotal": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "yyyy-MM-dd HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "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": 300, + "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" + }, + "title": "{i18n:api-usage.processing-failures-and-timeouts}", + "dropShadow": true, + "enableFullscreen": true, + "titleStyle": 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" + }, + "row": 0, + "col": 0, + "id": "2ee89893-4e38-5331-95b7-3fd4f310c5a7" } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "#666666", - "padding": "0", - "settings": { - "cardHtml": "
\n \n \n
\n
\n
\n
${title}
\n
${apiState}
\n
\n
\n
${unit}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
", - "cardCss": ".card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 13px 13px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.card .action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 10px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} \n\n\n" - }, - "title": "Alarm created", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": { - "cursor": "default" - }, - "titleStyle": { - "fontSize": "20px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "showLegend": false, - "actions": { - "elementClick": [ - { - "name": "email_messages_details", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "alarms_created", - "setEntityId": false, - "stateEntityParamName": null, - "openInSeparateDialog": null, - "dialogTitle": null, - "dialogHideDashboardToolbar": true, - "dialogWidth": null, - "dialogHeight": null, - "openRightLayout": false, - "id": "946ba769-84ac-1507-6baa-94701de8967b" - } - ] - }, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" }, - "row": 0, - "col": 0, - "id": "a151ae60-0326-6116-d818-9070dda8e9c7", - "typeFullFqn": "system.cards.html_value_card" - }, - "68e16e98-0420-f72c-4848-41dedffd3904": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "ruleEngineExecutionCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.rule-engine-executions}", - "color": "#ab00ff", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true + "states": { + "default": { + "name": "{i18n:api-usage.api-usage}", + "root": true, + "layouts": { + "main": { + "widgets": { + "aab68ab5-8e40-8694-c55c-8eb1c89b88fb": { + "sizeX": 4, + "sizeY": 2, + "row": 0, + "col": 0 + }, + "a84fa70a-ddfa-3b24-9aa4-cf9ce91f919a": { + "sizeX": 4, + "sizeY": 2, + "row": 0, + "col": 4 + }, + "d70d26d4-e22d-4ca9-9ea7-f9c87c093321": { + "sizeX": 4, + "sizeY": 2, + "row": 0, + "col": 8 + }, + "4d3ea95c-3188-9872-1817-2f989c7729e0": { + "sizeX": 4, + "sizeY": 2, + "row": 0, + "col": 12 + }, + "2d0d6ff6-cd59-51d4-b916-38e22cdd0702": { + "sizeX": 4, + "sizeY": 2, + "row": 0, + "col": 16 + }, + "120573cc-e246-eb49-7d80-68e5d3b3c0cc": { + "sizeX": 4, + "sizeY": 2, + "row": 0, + "col": 20 + }, + "63f99d90-23ab-f8c2-3290-1e693ded5a2e": { + "sizeX": 8, + "sizeY": 4, + "row": 2, + "col": 0 + }, + "a2b7e906-2d8a-41a8-99a6-409531bfa743": { + "sizeX": 8, + "sizeY": 4, + "row": 2, + "col": 8 + }, + "ca996b66-ab7e-f977-152c-98e4ebf2a901": { + "sizeX": 8, + "sizeY": 4, + "row": 2, + "col": 16 + }, + "a3c2f1bb-7d3a-f11c-7b3d-28cd84fdfe34": { + "sizeX": 8, + "sizeY": 4, + "row": 6, + "col": 0 + }, + "5cebd4f1-ff6e-62f9-025c-8e7583c3d66a": { + "sizeX": 8, + "sizeY": 4, + "row": 6, + "col": 8 + }, + "bc0c8840-a9b5-5583-de7b-9e9450f5d8fe": { + "sizeX": 8, + "sizeY": 4, + "row": 6, + "col": 16 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 5, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 100, + "outerMargin": true + } } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 86400000, - "interval": 3600000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 2400000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.rule-engine-hourly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": { - "headerButton": [ - { - "name": "{i18n:api-usage.view-statistics}", - "icon": "show_chart", - "type": "openDashboardState", - "targetDashboardStateId": "rule_engine_statistics", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "id": "f9f08190-9ed9-d802-5b7a-c57ff84b5648" - }, - { - "name": "{i18n:api-usage.view-details}", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "rule_engine_execution", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "id": "1aec196b-44ba-ddf4-c4dc-c3f60c1eb6fc" - } - ] - }, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "68e16e98-0420-f72c-4848-41dedffd3904", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "2aa6b499-6e27-b315-6833-89c4d58485ce": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "transportMsgCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.transport-messages}", - "color": "#2196f3", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "transportDataPointsCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.transport-data-points}", - "color": "#4caf50", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true + "transport": { + "name": "{i18n:api-usage.transport}", + "root": false, + "layouts": { + "main": { + "widgets": { + "0b091dc3-eec3-847e-d0ad-fdf12d474e7a": { + "sizeX": 24, + "sizeY": 6, + "row": 0, + "col": 0 + }, + "536d7104-49f8-fde6-5827-61b8419f15ec": { + "sizeX": 24, + "sizeY": 6, + "row": 6, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 5, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } } - }, - "_hash": 0.46849996721308895, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 86400000, - "interval": 3600000 }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" + "rule_engine_execution": { + "name": "{i18n:api-usage.rule-engine-executions}", + "root": false, + "layouts": { + "main": { + "widgets": { + "c77e417c-ad9d-8e23-3ea1-c75edd653bc0": { + "sizeX": 24, + "sizeY": 6, + "row": 0, + "col": 0 + }, + "870904d2-d2e1-a1b9-ce56-b03fd47259b5": { + "sizeX": 24, + "sizeY": 6, + "row": 6, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 5, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 + "telemetry_persistence": { + "name": "{i18n:api-usage.telemetry-persistence}", + "root": false, + "layouts": { + "main": { + "widgets": { + "7f4100d2-41be-4954-d353-1d45000dbbbb": { + "sizeX": 24, + "sizeY": 6, + "row": 0, + "col": 0 + }, + "226ef8c9-8488-3664-21ac-0b6217336202": { + "sizeX": 24, + "sizeY": 6, + "row": 6, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 5, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 + "rule_engine_statistics": { + "name": "{i18n:api-usage.rule-engine-statistics}", + "root": false, + "layouts": { + "main": { + "widgets": { + "a669cf86-e715-efa4-dd9a-b839abf499e9": { + "sizeX": 24, + "sizeY": 5, + "row": 7, + "col": 0 + }, + "fa938580-33db-f1b3-fafc-bc3e3784ad57": { + "sizeX": 12, + "sizeY": 7, + "row": 0, + "col": 0 + }, + "2ee89893-4e38-5331-95b7-3fd4f310c5a7": { + "sizeX": 12, + "sizeY": 7, + "row": 0, + "col": 12 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 5, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } }, - "stack": true, - "tooltipIndividual": false, - "defaultBarWidth": 2400000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true + "notifications": { + "name": "{i18n:api-usage.notifications-email-sms}", + "root": false, + "layouts": { + "main": { + "widgets": { + "36fdf999-ca22-9a4c-269d-3f004d792792": { + "sizeX": 12, + "sizeY": 6, + "row": 0, + "col": 0 + }, + "9a191755-499d-535e-86c5-061102729c02": { + "sizeX": 12, + "sizeY": 6, + "row": 0, + "col": 12 + }, + "4b266318-8357-33ef-ca5a-74cbf90e014f": { + "sizeX": 12, + "sizeY": 6, + "row": 6, + "col": 0 + }, + "5aa33b0b-3bd5-7fe7-ee72-f564c2ca79d8": { + "sizeX": 12, + "sizeY": 6, + "row": 6, + "col": 12 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 5, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } }, - "tooltipCumulative": false, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.transport-hourly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": { - "headerButton": [ - { - "name": "{i18n:api-usage.view-details}", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "transport", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "id": "6ef12f6a-0266-25cf-6ca5-5dcb772252c6" - } - ] - }, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "2aa6b499-6e27-b315-6833-89c4d58485ce", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "d890cea3-fba0-6474-9a21-fa780230dc62": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "jsExecutionCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.javascript-executions}", - "color": "#ff9900", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true + "alarms_created": { + "name": "{i18n:api-usage.alarms-created}", + "root": false, + "layouts": { + "main": { + "widgets": { + "bef6c27b-9fe7-ee92-40d9-9696c501a1f9": { + "sizeX": 24, + "sizeY": 6, + "row": 0, + "col": 0 + }, + "52305cf8-2258-5745-a0e7-41a171594bb3": { + "sizeX": 24, + "sizeY": 6, + "row": 6, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 5, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "tbelExecutionCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.tbel-executions}", - "color": "#4caf50", - "settings": {}, - "_hash": 0.6818645685001823, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 86400000, - "interval": 3600000 }, - "aggregation": { - "type": "NONE", - "limit": 1000 + "script_functions": { + "name": "{i18n:api-usage.scripts}", + "root": false, + "layouts": { + "main": { + "widgets": { + "c66e5060-57fd-11e7-6616-65b82c294ac2": { + "sizeX": 24, + "sizeY": 6, + "row": 0, + "col": 0 + }, + "d0e8603e-5d2e-9287-e2c6-8ccbe9c66806": { + "sizeX": 24, + "sizeY": 6, + "row": 6, + "col": 0 + } + }, + "gridSettings": { + "backgroundColor": "#eeeeee", + "color": "rgba(0,0,0,0.870588)", + "columns": 24, + "margin": 5, + "backgroundSizeMode": "100%", + "autoFillHeight": true, + "backgroundImageUrl": null, + "mobileAutoFillHeight": false, + "mobileRowHeight": 70, + "outerMargin": true + } + } + } } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "stack": false, - "enableSelection": true, - "fontSize": 10, - "fontColor": "#545454", - "showTooltip": true, - "tooltipIndividual": false, - "tooltipCumulative": false, - "hideZeros": false, - "grid": { - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1, - "color": "#545454", - "backgroundColor": null, - "tickColor": "#DDDDDD" - }, - "xaxis": { - "title": null, - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "min": 0, - "max": null, - "title": null, - "showLabels": true, - "color": "#545454", - "tickSize": null, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "defaultBarWidth": 2400000, - "barAlignment": "left", - "comparisonEnabled": false, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - }, - "customLegendEnabled": false - }, - "title": "{i18n:api-usage.scripts-hourly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": { - "headerButton": [ - { - "name": "{i18n:api-usage.view-details}", - "icon": "insert_chart", - "useShowWidgetActionFunction": null, - "showWidgetActionFunction": "return true;", - "type": "openDashboardState", - "targetDashboardStateId": "script_functions", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "openInSeparateDialog": false, - "openInPopover": false, - "id": "4687d3f6-8800-a3b6-26e5-0d33f3b828a9" - } - ] - }, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "", - "configMode": "advanced" }, - "row": 0, - "col": 0, - "id": "d890cea3-fba0-6474-9a21-fa780230dc62", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "84b6cfa5-1449-e0f2-3560-f810d2dd7ead": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "storageDataPointsCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.data-points-storage-days}", - "color": "#1039ee", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null + "entityAliases": { + "40193437-33ac-3172-eefd-0b08eb849062": { + "id": "40193437-33ac-3172-eefd-0b08eb849062", + "alias": "Api usage state", + "filter": { + "type": "apiUsageState", + "resolveMultiple": false + } + }, + "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f": { + "id": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f", + "alias": "TbServiceQueues", + "filter": { + "type": "assetType", + "resolveMultiple": true, + "assetNameFilter": "", + "assetTypes": [ + "TbServiceQueue" + ] } - ] } - ], - "timewindow": { + }, + "filters": {}, + "timewindow": { "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, "hideAggregation": false, "hideAggInterval": false, + "hideTimezone": false, "selectedTab": 0, "realtime": { - "timewindowMs": 86400000, - "interval": 3600000 + "realtimeType": 0, + "timewindowMs": 86400000, + "quickInterval": "CURRENT_DAY", + "interval": 3600000 }, "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 2400000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.telemetry-persistence-hourly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": { - "headerButton": [ - { - "name": "{i18n:api-usage.view-details}", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "telemetry_persistence", - "setEntityId": false, - "stateEntityParamName": null, - "openRightLayout": false, - "id": "16707efb-e572-bd02-c219-55fc1b0f672a" - } - ] - }, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "84b6cfa5-1449-e0f2-3560-f810d2dd7ead", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "d296b566-a000-7402-ae9d-c815381c5435": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "createdAlarmsCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.alarms-created}", - "color": "#d35a00", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 86400000, - "interval": 3600000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 2400000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.alarms-created-hourly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": { - "headerButton": [ - { - "name": "{i18n:api-usage.view-details}", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "alarms_created", - "setEntityId": false, - "stateEntityParamName": null, - "openInSeparateDialog": null, - "dialogTitle": null, - "dialogHideDashboardToolbar": true, - "dialogWidth": null, - "dialogHeight": null, - "openRightLayout": false, - "id": "371882f9-ea23-3abc-fca8-9449c5dfdd6b" - } - ] - }, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "d296b566-a000-7402-ae9d-c815381c5435", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "00a02464-9509-911b-3b5e-21fb37629822": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "emailCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.email-messages}", - "color": "#4caf50", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.1348755140779876, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "smsCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.sms-messages}", - "color": "#f36021", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 86400000, - "interval": 3600000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 2400000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.notifications-hourly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": { - "headerButton": [ - { - "name": "{i18n:api-usage.view-details}", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "notifications", - "setEntityId": false, - "stateEntityParamName": null, - "openInSeparateDialog": null, - "dialogTitle": null, - "dialogHideDashboardToolbar": true, - "dialogWidth": null, - "dialogHeight": null, - "openRightLayout": false, - "id": "49aefac0-ec5e-d6f3-f39c-8744759f4b19" - } - ] - }, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "00a02464-9509-911b-3b5e-21fb37629822", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "74199074-7873-6c6a-2a51-3fc614769f03": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "ruleEngineExecutionCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.rule-engine-executions}", - "color": "#ab00ff", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000 - }, - "aggregation": { - "type": "SUM", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 1800000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.rule-engine-daily-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "74199074-7873-6c6a-2a51-3fc614769f03", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "00006bc3-8a8d-b55e-ed39-e318f1bcd090": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "ruleEngineExecutionCount", - "type": "timeseries", - "label": "{i18n:api-usage.rule-engine-executions}", - "color": "#ab00ff", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 31536000000, - "interval": 1000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 900000000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.rule-engine-monthly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "00006bc3-8a8d-b55e-ed39-e318f1bcd090", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "5f5ca59c-e507-5301-5910-7ad8cd34df40": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "jsExecutionCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.javascript-executions}", - "color": "#ff9900", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "tbelExecutionCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.tbel-executions}", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5212969314724616, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000 - }, - "aggregation": { - "type": "SUM", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 1800000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.scripts-daily-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "5f5ca59c-e507-5301-5910-7ad8cd34df40", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "ada32ee9-44ed-48d1-c368-fd0c94b7607f": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "jsExecutionCount", - "type": "timeseries", - "label": "{i18n:api-usage.javascript-executions}", - "color": "#ff9900", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "tbelExecutionCount", - "type": "timeseries", - "label": "{i18n:api-usage.tbel-executions}", - "color": "#4caf50", - "settings": {}, - "_hash": 0.49748239768082403, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 31536000000, - "interval": 1000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 900000000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.scripts-monthly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "widgetCss": "", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "ada32ee9-44ed-48d1-c368-fd0c94b7607f", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "98bae68b-0f35-72f2-a428-9b06889f1554": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "transportMsgCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.transport-messages}", - "color": "#2196f3", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "transportDataPointsCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.transport-data-points}", - "color": "#4caf50", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.46849996721308895, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000 - }, - "aggregation": { - "type": "SUM", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": true, - "tooltipIndividual": false, - "defaultBarWidth": 1800000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "tooltipCumulative": false, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.transport-daily-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "98bae68b-0f35-72f2-a428-9b06889f1554", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "e61e5477-5a09-cc25-966b-f613d81da833": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "transportMsgCount", - "type": "timeseries", - "label": "{i18n:api-usage.transport-messages}", - "color": "#2196f3", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "transportDataPointsCount", - "type": "timeseries", - "label": "{i18n:api-usage.transport-data-points}", - "color": "#4caf50", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.46849996721308895, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 31536000000, - "interval": 2592000000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;", - "min": 0, - "tickDecimals": 0 - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": true, - "tooltipIndividual": false, - "defaultBarWidth": 900000000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "tooltipCumulative": false, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.transport-monthly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "e61e5477-5a09-cc25-966b-f613d81da833", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "85fe0738-5326-f069-ab3f-30594bde5fed": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "storageDataPointsCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.data-points-storage-days}", - "color": "#1039ee", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000 - }, - "aggregation": { - "type": "SUM", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 1800000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.telemetry-persistence-daily-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "85fe0738-5326-f069-ab3f-30594bde5fed", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "eaeb381a-437e-f6e9-60c9-6bc8826fdd44": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "storageDataPointsCount", - "type": "timeseries", - "label": "{i18n:api-usage.data-points-storage-days}", - "color": "#1039ee", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 31536000000, - "interval": 1000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 900000000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.telemetry-persistence-monthly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "eaeb381a-437e-f6e9-60c9-6bc8826fdd44", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "4b798823-b97d-9d6a-59dc-fcafd897fc23": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "createdAlarmsCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.alarms-created}", - "color": "#d35a00", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000 - }, - "aggregation": { - "type": "SUM", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 1800000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.alarms-created-daily-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "4b798823-b97d-9d6a-59dc-fcafd897fc23", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "6a981580-7490-19dd-f937-b64cbf67a982": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "createdAlarmsCount", - "type": "timeseries", - "label": "{i18n:api-usage.alarms-created}", - "color": "#d35a00", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 31536000000, - "interval": 1000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" + "type": "NONE", + "limit": 50000 }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 900000000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.alarms-created-monthly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" + "timezone": null }, - "row": 0, - "col": 0, - "id": "6a981580-7490-19dd-f937-b64cbf67a982", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "7302df65-1b0c-579e-bbdb-145126ae3392": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "smsCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.sms-messages}", - "color": "#f36021", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000 - }, - "aggregation": { - "type": "SUM", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 1800000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.sms-messages-daily-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "7302df65-1b0c-579e-bbdb-145126ae3392", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "fdb385e7-14fe-fc9f-ebdc-b400f26fc66b": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "smsCount", - "type": "timeseries", - "label": "{i18n:api-usage.sms-messages}", - "color": "#f36021", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 31536000000, - "interval": 1000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 900000000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.sms-messages-monthly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "fdb385e7-14fe-fc9f-ebdc-b400f26fc66b", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "2408ad30-163e-8221-08e1-a82b638be564": { - "type": "timeseries", - "sizeX": 12, - "sizeY": 7, - "config": { - "datasources": [ - { - "type": "entity", - "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f", - "dataKeys": [ - { - "name": "successfulMsgs", - "type": "timeseries", - "label": "{i18n:api-usage.successful}", - "color": "#4caf50", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.15490750967648736 - }, - { - "name": "failedMsgs", - "type": "timeseries", - "label": "{i18n:api-usage.permanent-failures}", - "color": "#ef5350", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.4186621166514697 - }, - { - "name": "tmpFailed", - "type": "timeseries", - "label": "{i18n:api-usage.processing-failures}", - "color": "#ffc107", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.49891007198715376 - } - ], - "latestDataKeys": [ - { - "name": "queueName", - "type": "entityField", - "label": "Queue name", - "color": "#ffc107", - "settings": {}, - "_hash": 0.11177210715294739 - }, - { - "name": "serviceId", - "type": "entityField", - "label": "Service Id", - "color": "#607d8b", - "settings": {}, - "_hash": 0.0001434357629259697 - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 3600000, - "interval": 1000 - }, - "aggregation": { - "type": "NONE", - "limit": 10000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "showMin": true, - "showMax": true, - "showAvg": false, - "showTotal": true - } - }, - "title": "Queue Stats", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "mobileHeight": null, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "widgetStyle": {}, - "useDashboardTimewindow": false, - "displayTimewindow": true, - "actions": {} - }, - "id": "2408ad30-163e-8221-08e1-a82b638be564", - "typeFullFqn": "system.charts.basic_timeseries" - }, - "e43dcfe1-b970-6a11-ce0e-5769f3eb5e88": { - "type": "timeseries", - "sizeX": 12, - "sizeY": 7, - "config": { - "datasources": [ - { - "type": "entity", - "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f", - "dataKeys": [ - { - "name": "timeoutMsgs", - "type": "timeseries", - "label": "{i18n:api-usage.permanent-timeouts}", - "color": "#4caf50", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.565222981550328 - }, - { - "name": "tmpTimeout", - "type": "timeseries", - "label": "{i18n:api-usage.processing-timeouts}", - "color": "#9c27b0", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": true, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.2679547062508352 - } - ], - "latestDataKeys": [ - { - "name": "queueName", - "type": "entityField", - "label": "Queue name", - "color": "#f44336", - "settings": {}, - "_hash": 0.20402776006381784 - }, - { - "name": "serviceId", - "type": "entityField", - "label": "Service Id", - "color": "#ffc107", - "settings": {}, - "_hash": 0.3926008488095458 - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 3600000, - "interval": 1000 - }, - "aggregation": { - "type": "NONE", - "limit": 10000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "showMin": true, - "showMax": true, - "showAvg": false, - "showTotal": true - } - }, - "title": "Processing Failures and Timeouts", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "mobileHeight": null, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "widgetStyle": {}, - "useDashboardTimewindow": false, - "displayTimewindow": true, - "actions": {} - }, - "id": "e43dcfe1-b970-6a11-ce0e-5769f3eb5e88", - "typeFullFqn": "system.charts.basic_timeseries" - }, - "a669cf86-e715-efa4-dd9a-b839abf499e9": { - "type": "timeseries", - "sizeX": 24, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "entityAliasId": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f", - "dataKeys": [ - { - "name": "ruleEngineException", - "type": "timeseries", - "label": "Rule Chain", - "color": "#2196f3", - "settings": { - "useCellStyleFunction": false, - "useCellContentFunction": true, - "cellContentFunction": "return JSON.parse(value).ruleChainName;" - }, - "_hash": 0.9954481282345906 - }, - { - "name": "ruleEngineException", - "type": "timeseries", - "label": "Rule Node", - "color": "#4caf50", - "settings": { - "useCellStyleFunction": false, - "useCellContentFunction": true, - "cellContentFunction": "return JSON.parse(value).ruleNodeName;" - }, - "_hash": 0.18580357036589978 - }, - { - "name": "ruleEngineException", - "type": "timeseries", - "label": "Latest Error", - "color": "#f44336", - "settings": { - "useCellStyleFunction": false, - "useCellContentFunction": true, - "cellContentFunction": "return JSON.parse(value).message;" - }, - "_hash": 0.7255162989552142 - } - ], - "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.5285486822242347, - "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.2512602689087131, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 2592000000, - "interval": 1000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "rgb(255, 255, 255)", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "showTimestamp": true, - "displayPagination": true, - "defaultPageSize": 10 - }, - "title": "Exceptions", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": false, - "showLegend": false, - "widgetStyle": {}, - "actions": {}, - "showTitleIcon": false, - "titleIcon": null, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "", - "displayTimewindow": true - }, - "id": "a669cf86-e715-efa4-dd9a-b839abf499e9", - "typeFullFqn": "system.cards.timeseries_table" - }, - "292eaded-4775-36f7-c896-98d57bdda936": { - "type": "latest", - "sizeX": 7.5, - "sizeY": 3, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "emailApiState", - "type": "timeseries", - "label": "apiState", - "color": "#2196f3", - "settings": {}, - "_hash": 0.8830669138660703, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "emailLimit", - "type": "timeseries", - "label": "limit", - "color": "#4caf50", - "settings": {}, - "_hash": 0.5463603803546802, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "emailCount", - "type": "timeseries", - "label": "count", - "color": "#f44336", - "settings": {}, - "_hash": 0.5564241862015964, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "transportApiState", - "type": "timeseries", - "label": "cardId", - "color": "#607d8b", - "settings": {}, - "_hash": 0.051659774305067296, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return (Math.random()*100000).toFixed(0);" - }, - { - "name": "smsApiState", - "type": "timeseries", - "label": "apiStatePoint", - "color": "#e91e63", - "settings": {}, - "_hash": 0.2969682764607864, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "smsLimit", - "type": "timeseries", - "label": "pointsLimit", - "color": "#9c27b0", - "settings": {}, - "_hash": 0.22082255831864894, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "smsCount", - "type": "timeseries", - "label": "pointsCount", - "color": "#8bc34a", - "settings": {}, - "_hash": 0.6340356364819146, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "transportApiState", - "type": "timeseries", - "label": "title", - "color": "#3f51b5", - "settings": {}, - "_hash": 0.6894070537030252, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": true, - "postFuncBody": "return \"{i18n:api-usage.notifications}\";" - } - ] - } - ], - "timewindow": { - "displayValue": "", - "selectedTab": 0, - "realtime": { - "realtimeType": 1, - "interval": 1000, - "timewindowMs": 60000, - "quickInterval": "CURRENT_DAY" - }, - "history": { - "historyType": 0, - "interval": 1000, - "timewindowMs": 60000, - "fixedTimewindow": { - "startTimeMs": 1694083375827, - "endTimeMs": 1694169775827 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "#666666", - "padding": "0", - "settings": { - "cardCss": ".card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 13px 13px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bars-row {\n flex: 1;\n display: flex;\n flex-direction: row;\n}\n\n.card .bar-column {\n flex: 1;\n display: flex;\n flex-direction: column;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.card .action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 6px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} \n\n", - "cardHtml": "
\n \n \n
\n
\n
\n
\n ${title}\n
\n
\n
\n
\n
\n
\n
{i18n:api-usage.email}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
{i18n:api-usage.sms}
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n \n
\n
" - }, - "title": "Notifications (Email/SMS)", - "dropShadow": true, - "enableFullscreen": false, - "widgetStyle": { - "cursor": "default" - }, - "titleStyle": { - "fontSize": "20px", - "fontWeight": 400 - }, - "useDashboardTimewindow": true, - "showLegend": false, - "actions": { - "elementClick": [ - { - "name": "transport_details", - "icon": "insert_chart", - "type": "openDashboardState", - "targetDashboardStateId": "notifications", - "setEntityId": false, - "stateEntityParamName": null, - "openInSeparateDialog": null, - "dialogTitle": null, - "dialogHideDashboardToolbar": true, - "dialogWidth": null, - "dialogHeight": null, - "openRightLayout": false, - "id": "46b7cefe-e1f2-67c1-4055-3a214520f869" - } - ] - }, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "292eaded-4775-36f7-c896-98d57bdda936", - "typeFullFqn": "system.cards.html_value_card" - }, - "b3571a36-2106-1122-7d58-0d38bbb0e9c8": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "emailCountHourly", - "type": "timeseries", - "label": "{i18n:api-usage.email-messages}", - "color": "#d35a00", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000 - }, - "aggregation": { - "type": "SUM", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 1800000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.email-messages-daily-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "b3571a36-2106-1122-7d58-0d38bbb0e9c8", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, - "aa875b7f-e7c8-7529-1ae7-f456211b59cc": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "40193437-33ac-3172-eefd-0b08eb849062", - "filterId": null, - "dataKeys": [ - { - "name": "emailCount", - "type": "timeseries", - "label": "{i18n:api-usage.email-messages}", - "color": "#d35a00", - "settings": { - "excludeFromStacking": false, - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "showLines": false, - "fillLines": false, - "showPoints": false, - "showPointShape": "circle", - "pointShapeFormatter": "", - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showSeparateAxis": false, - "axisPosition": "left", - "thresholds": [ - { - "thresholdValueSource": "predefinedValue" - } - ], - "comparisonSettings": { - "showValuesForComparison": true - } - }, - "_hash": 0.0661644137210089, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ] - } - ], - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 31536000000, - "interval": 1000 - }, - "aggregation": { - "type": "NONE", - "limit": 1000 - } - }, - "showTitle": true, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "8px", - "settings": { - "shadowSize": 4, - "fontColor": "#545454", - "fontSize": 10, - "xaxis": { - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "showLabels": true, - "color": "#545454", - "min": 0, - "tickDecimals": 0, - "ticksFormatter": "var rounder = Math.pow(10, 1);\nvar powers = [\n {key: 'Q', value: Math.pow(10, 15)},\n {key: 'T', value: Math.pow(10, 12)},\n {key: 'B', value: Math.pow(10, 9)},\n {key: 'M', value: Math.pow(10, 6)},\n {key: 'K', value: 1000}\n];\n\nvar key = '';\n\nfor (var i = 0; i < powers.length; i++) {\n var reduced = value / powers[i].value;\n reduced = Math.round(reduced * rounder) / rounder;\n if (reduced >= 1) {\n value = reduced;\n key = powers[i].key;\n break;\n }\n}\nreturn value + key;" - }, - "grid": { - "color": "#545454", - "tickColor": "#DDDDDD", - "verticalLines": true, - "horizontalLines": true, - "outlineWidth": 1 - }, - "stack": false, - "tooltipIndividual": false, - "defaultBarWidth": 900000000, - "barAlignment": "left", - "timeForComparison": "months", - "xaxisSecond": { - "axisPosition": "top", - "showLabels": true - }, - "showLegend": true, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": true - } - }, - "title": "{i18n:api-usage.email-messages-monthly-activity}", - "dropShadow": true, - "enableFullscreen": true, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": {}, - "useDashboardTimewindow": false, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "iconColor": "rgba(0, 0, 0, 0.87)", - "iconSize": "24px", - "titleTooltip": "" - }, - "row": 0, - "col": 0, - "id": "aa875b7f-e7c8-7529-1ae7-f456211b59cc", - "typeFullFqn": "system.charts.timeseries_bars_flot" - } - }, - "states": { - "default": { - "name": "{i18n:api-usage.api-usage}", - "root": true, - "layouts": { - "main": { - "widgets": { - "fd6df872-2ddf-0921-3929-2e7f55062fad": { - "sizeX": 4, - "sizeY": 2, - "row": 0, - "col": 8, - "mobileHeight": 3 - }, - "7e235874-461b-e7c2-2fdd-d8762a020773": { - "sizeX": 4, - "sizeY": 2, - "row": 0, - "col": 12, - "mobileHeight": 3 - }, - "08545554-a0e8-05c7-66df-6000cfeff8a4": { - "sizeX": 4, - "sizeY": 2, - "row": 0, - "col": 4, - "mobileHeight": 3 - }, - "a245c67e-53ec-d299-fa89-69fe2062ccb2": { - "sizeX": 4, - "sizeY": 2, - "row": 0, - "col": 0, - "mobileHeight": 3 - }, - "a151ae60-0326-6116-d818-9070dda8e9c7": { - "sizeX": 4, - "sizeY": 2, - "row": 0, - "col": 16, - "mobileHeight": 3 - }, - "292eaded-4775-36f7-c896-98d57bdda936": { - "sizeX": 4, - "sizeY": 2, - "row": 0, - "col": 20, - "mobileHeight": 3 - }, - "68e16e98-0420-f72c-4848-41dedffd3904": { - "sizeX": 8, - "sizeY": 4, - "row": 2, - "col": 8 - }, - "2aa6b499-6e27-b315-6833-89c4d58485ce": { - "sizeX": 8, - "sizeY": 4, - "row": 2, - "col": 0 - }, - "d890cea3-fba0-6474-9a21-fa780230dc62": { - "sizeX": 8, - "sizeY": 4, - "row": 2, - "col": 16 - }, - "84b6cfa5-1449-e0f2-3560-f810d2dd7ead": { - "sizeX": 8, - "sizeY": 4, - "row": 6, - "col": 0 - }, - "d296b566-a000-7402-ae9d-c815381c5435": { - "sizeX": 8, - "sizeY": 4, - "row": 6, - "col": 8 - }, - "00a02464-9509-911b-3b5e-21fb37629822": { - "sizeX": 8, - "sizeY": 4, - "row": 6, - "col": 16 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 5, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70, - "outerMargin": true - } - } - } - }, - "transport": { - "name": "{i18n:api-usage.transport}", - "root": false, - "layouts": { - "main": { - "widgets": { - "98bae68b-0f35-72f2-a428-9b06889f1554": { - "sizeX": 24, - "sizeY": 6, - "row": 0, - "col": 0 - }, - "e61e5477-5a09-cc25-966b-f613d81da833": { - "sizeX": 24, - "sizeY": 6, - "row": 6, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 10, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70, - "outerMargin": true - } - } - } - }, - "rule_engine_execution": { - "name": "{i18n:api-usage.rule-engine-executions}", - "root": false, - "layouts": { - "main": { - "widgets": { - "74199074-7873-6c6a-2a51-3fc614769f03": { - "sizeX": 24, - "sizeY": 6, - "row": 0, - "col": 0 - }, - "00006bc3-8a8d-b55e-ed39-e318f1bcd090": { - "sizeX": 24, - "sizeY": 6, - "row": 6, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 10, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70, - "outerMargin": true - } - } - } - }, - "telemetry_persistence": { - "name": "{i18n:api-usage.telemetry-persistence}", - "root": false, - "layouts": { - "main": { - "widgets": { - "85fe0738-5326-f069-ab3f-30594bde5fed": { - "sizeX": 24, - "sizeY": 6, - "row": 0, - "col": 0 - }, - "eaeb381a-437e-f6e9-60c9-6bc8826fdd44": { - "sizeX": 24, - "sizeY": 6, - "row": 6, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 10, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70, - "outerMargin": true - } - } - } - }, - "rule_engine_statistics": { - "name": "{i18n:api-usage.rule-engine-statistics}", - "root": false, - "layouts": { - "main": { - "widgets": { - "2408ad30-163e-8221-08e1-a82b638be564": { - "sizeX": 12, - "sizeY": 7, - "mobileHeight": null, - "row": 0, - "col": 0 - }, - "e43dcfe1-b970-6a11-ce0e-5769f3eb5e88": { - "sizeX": 12, - "sizeY": 7, - "mobileHeight": null, - "row": 0, - "col": 12 - }, - "a669cf86-e715-efa4-dd9a-b839abf499e9": { - "sizeX": 24, - "sizeY": 5, - "row": 7, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 10, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70, - "outerMargin": true - } - } - } - }, - "notifications": { - "name": "{i18n:api-usage.notifications-email-sms}", - "root": false, - "layouts": { - "main": { - "widgets": { - "7302df65-1b0c-579e-bbdb-145126ae3392": { - "sizeX": 12, - "sizeY": 6, - "row": 0, - "col": 12 - }, - "fdb385e7-14fe-fc9f-ebdc-b400f26fc66b": { - "sizeX": 12, - "sizeY": 6, - "row": 6, - "col": 12 - }, - "b3571a36-2106-1122-7d58-0d38bbb0e9c8": { - "sizeX": 12, - "sizeY": 6, - "row": 0, - "col": 0 - }, - "aa875b7f-e7c8-7529-1ae7-f456211b59cc": { - "sizeX": 12, - "sizeY": 6, - "row": 6, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 10, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70, - "outerMargin": true - } - } - } - }, - "alarms_created": { - "name": "{i18n:api-usage.alarms-created}", - "root": false, - "layouts": { - "main": { - "widgets": { - "4b798823-b97d-9d6a-59dc-fcafd897fc23": { - "sizeX": 24, - "sizeY": 6, - "row": 0, - "col": 0 - }, - "6a981580-7490-19dd-f937-b64cbf67a982": { - "sizeX": 24, - "sizeY": 6, - "row": 6, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 10, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70, - "outerMargin": true - } - } + "settings": { + "stateControllerId": "entity", + "showTitle": false, + "showDashboardsSelect": false, + "showEntitiesSelect": false, + "showDashboardTimewindow": false, + "showDashboardExport": false, + "toolbarAlwaysOpen": true, + "titleColor": "rgba(0,0,0,0.870588)", + "showFilters": false, + "showDashboardLogo": false, + "dashboardLogoUrl": null, + "hideToolbar": false, + "showUpdateDashboardImage": false, + "dashboardCss": ".card .bars-row {\n flex: 1;\n display: flex;\n flex-direction: row;\n}\n\n.card .bar-column {\n flex: 1;\n display: flex;\n flex-direction: column;\n}\n\n\n.card {\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n display: flex;\n flex-direction: column;\n}\n\n.card > img {\n height: 0;\n}\n\n.card .content {\n flex: 1; \n padding: 12px 12px 0;\n display: flex;\n box-sizing: border-box;\n}\n\n.card .content .column {\n display: flex;\n flex-direction: column; \n justify-content: space-around;\n flex: 1;\n}\n\n.card .content .title-row {\n display: flex;\n flex-direction: row;\n padding-bottom: 10px;\n}\n\n.card .title {\n flex: 1;\n font-size: 20px;\n font-weight: 400;\n color: #666666;\n}\n\n.card .state {\n text-transform: uppercase;\n font-size: 20px;\n font-weight: bold;\n}\n\n.card.enabled .state {\n color: #00B260;\n}\n\n.card.warning .state {\n color: #FFAD6F;\n}\n\n.card.disabled .state {\n color: #F73243;\n}\n\n.card .bar-container {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n}\n\n.card .bar {\n flex: 1;\n max-height: 30px;\n margin-top: 3.5px;\n margin-bottom: 4px;\n background-color: #F0F0F0;\n border: 1px solid #DADCDB;\n border-radius: 2px;\n box-shadow: inset 0 1px 3px rgba(0, 0, 0, .2);\n}\n\n.card.enabled .bar {\n border-color: #00B260;\n background-color: #F0FBF7;\n}\n\n.card.warning .bar {\n border-color: #FFAD6F;\n background-color: #FFFAF6;\n}\n\n.card.disabled .bar {\n border-color: #F73243;\n background-color: #FFF0F0;\n}\n\n.card .bar .bar-fill {\n background-color: #F0F0F0;\n border-radius: 2px;\n height: 100%;\n width: 0%;\n}\n\n.card.enabled .bar-fill {\n background-color: #00C46C;\n}\n\n.card.warning .bar-fill {\n background-color: #FFD099;\n}\n\n.card.disabled .bar-fill {\n background-color: #FF9494;\n}\n\n.card .bar-labels {\n height: 20px;\n font-size: 16px;\n color: #666;\n display: flex;\n flex-direction: row;\n}\n\n\n.card .mat-mdc-button-base {\n text-transform: uppercase;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.card .mdc-button__label {\n pointer-events: none;\n}\n\n.action-row {\n display: flex;\n flex-direction: row;\n justify-content: flex-end;\n padding: 8px 0;\n}\n\n.card .unit {\n color: #666666;\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .card .title {\n font-size: 12px;\n }\n .card .state {\n font-size: 12px;\n }\n .card .unit {\n font-size: 8px;\n }\n .card .bar-labels {\n font-size: 8px;\n }\n .card .mat-mdc-button-base {\n font-size: 8px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1599px) {\n .card .title {\n font-size: 14px;\n }\n .card .state {\n font-size: 14px;\n }\n .card .unit {\n font-size: 10px;\n }\n .card .bar-labels {\n font-size: 10px;\n }\n .card .mat-mdc-button-base {\n font-size: 10px;\n }\n .card .action-row {\n padding: 0;\n }\n}\n\n@media screen and (min-width: 1600px) and (max-width: 1919px) {\n .card .title {\n font-size: 16px;\n }\n .card .state {\n font-size: 16px;\n }\n .card .unit {\n font-size: 12px;\n }\n .card .bar-labels {\n font-size: 12px;\n }\n .card .mat-mdc-button-base {\n font-size: 12px;\n }\n .card .action-row {\n padding: 0;\n }\n} " } - }, - "script_functions": { - "name": "{i18n:api-usage.scripts}", - "root": false, - "layouts": { - "main": { - "widgets": { - "5f5ca59c-e507-5301-5910-7ad8cd34df40": { - "sizeX": 24, - "sizeY": 6, - "row": 0, - "col": 0 - }, - "ada32ee9-44ed-48d1-c368-fd0c94b7607f": { - "sizeX": 24, - "sizeY": 6, - "row": 6, - "col": 0 - } - }, - "gridSettings": { - "backgroundColor": "#eeeeee", - "color": "rgba(0,0,0,0.870588)", - "columns": 24, - "margin": 10, - "backgroundSizeMode": "100%", - "autoFillHeight": true, - "backgroundImageUrl": null, - "mobileAutoFillHeight": false, - "mobileRowHeight": 70, - "outerMargin": true - } - } - } - } - }, - "entityAliases": { - "40193437-33ac-3172-eefd-0b08eb849062": { - "id": "40193437-33ac-3172-eefd-0b08eb849062", - "alias": "Api usage state", - "filter": { - "type": "apiUsageState", - "resolveMultiple": false - } - }, - "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f": { - "id": "2e4c97b0-257a-a1b9-690c-141d9bf2ec6f", - "alias": "TbServiceQueues", - "filter": { - "type": "entityType", - "resolveMultiple": true, - "entityType": "QUEUE_STATS" - } - } - }, - "filters": {}, - "timewindow": { - "hideInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "selectedTab": 0, - "realtime": { - "timewindowMs": 86400000, - "interval": 3600000 - }, - "aggregation": { - "type": "NONE", - "limit": 50000 - } }, - "settings": { - "stateControllerId": "entity", - "showTitle": false, - "showDashboardsSelect": false, - "showEntitiesSelect": false, - "showDashboardTimewindow": false, - "showDashboardExport": false, - "toolbarAlwaysOpen": true, - "titleColor": "rgba(0,0,0,0.870588)", - "showFilters": false, - "showDashboardLogo": false - } - }, - "externalId": null, - "name": "Api Usage" + "name": "Api Usage" } diff --git a/ui-ngx/src/assets/dashboard/sys_admin_home_page.json b/ui-ngx/src/assets/dashboard/sys_admin_home_page.json index f93de01596..29c692ad87 100644 --- a/ui-ngx/src/assets/dashboard/sys_admin_home_page.json +++ b/ui-ngx/src/assets/dashboard/sys_admin_home_page.json @@ -69,142 +69,6 @@ "id": "d70cc256-4c7b-ee06-9905-b8c5e546605f", "typeFullFqn": "system.cards.markdown_card" }, - "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", - "filterId": null, - "dataKeys": [ - { - "name": "transportMsgCountHourly", - "type": "timeseries", - "label": "{i18n:widgets.transport-messages.title}", - "color": "#305680", - "settings": {}, - "_hash": 0.2880464219129071, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ], - "latestDataKeys": [] - } - ], - "timewindow": { - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, - "hideAggregation": true, - "hideAggInterval": false, - "hideTimezone": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000, - "fixedTimewindow": { - "startTimeMs": 1680443065451, - "endTimeMs": 1680529465451 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "SUM", - "limit": 50000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "0px", - "settings": { - "stack": true, - "fontSize": 10, - "fontColor": "#545454", - "showTooltip": true, - "tooltipIndividual": false, - "tooltipCumulative": false, - "hideZeros": false, - "grid": { - "verticalLines": false, - "horizontalLines": false, - "outlineWidth": 0, - "color": "#545454", - "backgroundColor": null, - "tickColor": "#DDDDDD" - }, - "xaxis": { - "title": null, - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "min": 0, - "max": null, - "title": null, - "showLabels": true, - "color": "#545454", - "tickSize": null, - "tickDecimals": 0, - "ticksFormatter": "return value % 1000 === 0 ? ((value / 1000) + 'k') : '';" - }, - "defaultBarWidth": 1800000, - "barAlignment": "left", - "comparisonEnabled": false, - "timeForComparison": "previousInterval", - "comparisonCustomIntervalValue": 7200000, - "xaxisSecond": { - "axisPosition": "top", - "title": null, - "showLabels": true - }, - "customLegendEnabled": false, - "dataKeysListForLabels": [], - "showLegend": false, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": true, - "showTotal": false, - "showLatest": false - } - }, - "title": "Transport messages", - "dropShadow": false, - "enableFullscreen": false, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": { - "padding": "0" - }, - "useDashboardTimewindow": false, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "titleTooltip": "", - "widgetCss": ".tb-widget-container > .tb-widget {\n border: none !important;\n border-radius: 0 !important;\n box-shadow: none !important;\n}\n\n.tb-widget-container > .tb-widget .flot-base {\n opacity: 0.48;\n}\n", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, "4b5e47ed-c197-a937-d727-041ba8decec2": { "type": "latest", "sizeX": 5, @@ -294,227 +158,6 @@ "id": "4b5e47ed-c197-a937-d727-041ba8decec2", "typeFullFqn": "system.cards.markdown_card" }, - "eace9148-b02a-48fe-1a95-acd9928aa8c5": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", - "filterId": null, - "dataKeys": [ - { - "name": "cpuUsage", - "type": "timeseries", - "label": "{i18n:widgets.system-info.cpu}", - "color": "#305680", - "settings": { - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "excludeFromStacking": false, - "showLines": true, - "lineWidth": 3, - "fillLines": false, - "showPoints": false, - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showSeparateAxis": false, - "axisPosition": "left", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - }, - "thresholds": [] - }, - "_hash": 0.9347575372081658, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "memoryUsage", - "type": "timeseries", - "label": "{i18n:widgets.system-info.ram}", - "color": "#ac3bc9", - "settings": { - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "excludeFromStacking": false, - "showLines": true, - "lineWidth": 3, - "fillLines": false, - "showPoints": false, - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showSeparateAxis": false, - "axisPosition": "left", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - }, - "thresholds": [] - }, - "_hash": 0.31887216598848855, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - }, - { - "name": "discUsage", - "type": "timeseries", - "label": "{i18n:widgets.system-info.disk}", - "color": "#40d0ae", - "settings": { - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "excludeFromStacking": false, - "showLines": true, - "lineWidth": 3, - "fillLines": false, - "showPoints": false, - "showPointsLineWidth": 5, - "showPointsRadius": 3, - "showPointShape": "circle", - "pointShapeFormatter": "var size = radius * Math.sqrt(Math.PI) / 2;\nctx.moveTo(x - size, y - size);\nctx.lineTo(x + size, y + size);\nctx.moveTo(x - size, y + size);\nctx.lineTo(x + size, y - size);", - "showSeparateAxis": false, - "axisPosition": "left", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - }, - "thresholds": [] - }, - "_hash": 0.26499182606431004, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ], - "latestDataKeys": null - } - ], - "timewindow": { - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, - "hideAggregation": false, - "hideAggInterval": false, - "hideTimezone": false, - "selectedTab": 0, - "realtime": { - "realtimeType": 0, - "timewindowMs": 3600000, - "quickInterval": "CURRENT_DAY", - "interval": 10000 - }, - "aggregation": { - "type": "AVG", - "limit": 25000 - } - }, - "showTitle": false, - "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, - "grid": { - "verticalLines": false, - "horizontalLines": false, - "outlineWidth": 0, - "color": "#545454", - "backgroundColor": null, - "tickColor": "#DDDDDD" - }, - "xaxis": { - "title": null, - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "min": 0, - "max": 100, - "title": null, - "showLabels": true, - "color": "#545454", - "tickSize": null, - "tickDecimals": 0, - "ticksFormatter": "" - }, - "shadowSize": 0, - "smoothLines": true, - "comparisonEnabled": false, - "timeForComparison": "previousInterval", - "comparisonCustomIntervalValue": 7200000, - "xaxisSecond": { - "axisPosition": "top", - "title": null, - "showLabels": true - }, - "customLegendEnabled": false, - "dataKeysListForLabels": [], - "showLegend": true, - "legendConfig": { - "direction": "row", - "position": "top", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": false, - "showTotal": false, - "showLatest": false - } - }, - "title": "System Info Chart", - "dropShadow": false, - "enableFullscreen": false, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": false, - "showTitleIcon": false, - "titleTooltip": "", - "widgetStyle": {}, - "widgetCss": "\n.tb-legend-keys td .tb-legend-line {\n height: 5px !important;\n vertical-align: middle;\n}\n", - "pageSize": 1024, - "units": "%", - "noDataDisplayMessage": "", - "displayTimewindow": true - }, - "row": 0, - "col": 0, - "id": "eace9148-b02a-48fe-1a95-acd9928aa8c5", - "typeFullFqn": "system.charts.basic_timeseries" - }, "8acbf5df-f9fc-114d-216f-86f081aa4779": { "type": "latest", "sizeX": 5, @@ -2197,6 +1840,800 @@ "col": 0, "id": "163025d8-3ca4-dd1e-c17b-e40d8e03e8a8", "typeFullFqn": "system.cards.markdown_card" + }, + "98bce47d-76df-5fc1-f8b9-8584d73a2439": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": "", + "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "dataKeys": [ + { + "name": "transportMsgCountHourly", + "type": "timeseries", + "label": "{i18n:widgets.transport-messages.title}", + "color": "#305680", + "settings": { + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "bar", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "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)", + "pointShape": "emptyCircle", + "pointSize": 4, + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.39181957822569946, + "decimals": 0 + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + }, + "latestDataKeys": [] + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": true, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000, + "fixedTimewindow": { + "startTimeMs": 1709807941444, + "endTimeMs": 1709894341444 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "SUM", + "limit": 25000 + }, + "timezone": null + }, + "showTitle": false, + "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": "return value === 0 ? value : ((value / 1000) + 'k');", + "showTicks": false, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": false, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": false, + "splitLinesColor": "rgba(0, 0, 0, 0.12)", + "id": "default", + "order": 0, + "min": 0, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "showTicks": false, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": false, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": false, + "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": false, + "legendLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendConfig": { + "direction": "column", + "position": "top", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "MMMM dd, yyyy", + "lastUpdateAgo": false, + "custom": true + }, + "tooltipDateFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipDateColor": "rgba(0, 0, 0, 0.76)", + "tooltipDateInterval": false, + "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", + "tooltipBackgroundBlur": 4, + "animation": { + "animation": true, + "animationThreshold": 2000, + "animationDuration": 1000, + "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": "0" + }, + "title": "Time series chart", + "dropShadow": false, + "enableFullscreen": false, + "titleStyle": 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": ".tb-widget-container > .tb-widget {\n border: none !important;\n border-radius: 0 !important;\n box-shadow: none !important;\n}\n\n.tb-time-series-chart-panel div.tb-widget-title {\n padding-top: 5px;\n padding-left: 5px;\n}", + "pageSize": 1024, + "units": "", + "decimals": null, + "noDataDisplayMessage": "", + "timewindowStyle": { + "showIcon": true, + "iconSize": "24px", + "icon": "query_builder", + "iconPosition": "left", + "font": { + "size": 14, + "sizeUnit": "px", + "family": "Roboto", + "weight": "400", + "style": "normal", + "lineHeight": "16px" + }, + "color": "rgba(0, 0, 0, 0.54)", + "displayTypePrefix": true + }, + "margin": "0px", + "borderRadius": "0px", + "iconSize": "0px" + }, + "row": 0, + "col": 0, + "id": "98bce47d-76df-5fc1-f8b9-8584d73a2439" + }, + "b9174a99-2a00-3dbe-0eae-927fd8506345": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": "", + "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "dataKeys": [ + { + "name": "cpuUsage", + "type": "timeseries", + "label": "{i18n:widgets.system-info.cpu}", + "color": "#305680", + "settings": { + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": true, + "lineType": "solid", + "lineWidth": 3, + "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)", + "pointShape": "circle", + "pointSize": 14, + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.39181957822569946, + "decimals": null, + "aggregationType": null, + "units": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "memoryUsage", + "type": "timeseries", + "label": "{i18n:widgets.system-info.ram}", + "color": "#AC3BC9", + "settings": { + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": true, + "lineType": "solid", + "lineWidth": 3, + "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)", + "pointShape": "circle", + "pointSize": 14, + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.9392996197028385, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + }, + { + "name": "discUsage", + "type": "timeseries", + "label": "{i18n:widgets.system-info.disk}", + "color": "#40D0AE", + "settings": { + "yAxisId": "default", + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": true, + "lineType": "solid", + "lineWidth": 3, + "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)", + "pointShape": "circle", + "pointSize": 14, + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.027335636460212864, + "aggregationType": null, + "units": null, + "decimals": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + }, + "latestDataKeys": [] + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": false, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 0, + "realtime": { + "realtimeType": 0, + "timewindowMs": 3600000, + "quickInterval": "CURRENT_DAY", + "interval": 10000 + }, + "aggregation": { + "type": "AVG", + "limit": 25000 + }, + "timezone": null + }, + "showTitle": false, + "backgroundColor": "rgb(255, 255, 255)", + "color": "rgba(0, 0, 0, 0.87)", + "padding": "8px", + "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": false, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": false, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": false, + "splitLinesColor": "rgba(0, 0, 0, 0.12)", + "id": "default", + "order": 0, + "min": 0, + "max": 100 + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "showTicks": false, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": false, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": false, + "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": "top", + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "dd MMM yyyy HH:mm:ss", + "lastUpdateAgo": false, + "custom": false + }, + "tooltipDateFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipDateColor": "rgba(0, 0, 0, 0.76)", + "tooltipDateInterval": false, + "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", + "tooltipBackgroundBlur": 4, + "animation": { + "animation": true, + "animationThreshold": 2000, + "animationDuration": 300, + "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": "0" + }, + "title": "Time series chart", + "dropShadow": false, + "enableFullscreen": false, + "titleStyle": 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": ".tb-time-series-chart-panel div.tb-widget-title {\n padding-top: 5px;\n padding-left: 5px;\n}", + "pageSize": 1024, + "units": "%", + "decimals": null, + "noDataDisplayMessage": "", + "timewindowStyle": { + "showIcon": true, + "iconSize": "24px", + "icon": "query_builder", + "iconPosition": "left", + "font": { + "size": 14, + "sizeUnit": "px", + "family": "Roboto", + "weight": "400", + "style": "normal", + "lineHeight": "16px" + }, + "color": "rgba(0, 0, 0, 0.54)", + "displayTypePrefix": true + }, + "margin": "0px", + "borderRadius": "", + "iconSize": "0px" + }, + "row": 0, + "col": 0, + "id": "b9174a99-2a00-3dbe-0eae-927fd8506345" } }, "states": { @@ -2288,7 +2725,7 @@ "layouts": { "main": { "widgets": { - "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e": { + "98bce47d-76df-5fc1-f8b9-8584d73a2439": { "sizeX": 24, "sizeY": 11, "row": 0, @@ -2315,14 +2752,6 @@ "layouts": { "main": { "widgets": { - "eace9148-b02a-48fe-1a95-acd9928aa8c5": { - "sizeX": 48, - "sizeY": 20, - "row": 8, - "col": 0, - "mobileOrder": 1, - "mobileHeight": 10 - }, "f0479b00-ed47-0a30-0c36-bde7847aae00": { "sizeX": 48, "sizeY": 8, @@ -2330,6 +2759,12 @@ "mobileHeight": 4, "row": 0, "col": 0 + }, + "b9174a99-2a00-3dbe-0eae-927fd8506345": { + "sizeX": 48, + "sizeY": 20, + "row": 8, + "col": 0 } }, "gridSettings": { @@ -2609,6 +3044,5 @@ "dashboardCss": ".tb-widget-container > .tb-widget {\n border: 1px solid rgba(0, 0, 0, 0.05);\n box-shadow: 0px 5px 16px rgba(0, 0, 0, 0.04);\n border-radius: 12px;\n}\n\n.tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 16px !important;\n}\n\n.tb-card-title {\n display: grid;\n}\n\n.tb-home-widget-title {\n font-style: normal;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n color: rgba(0, 0, 0, 0.54);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tb-home-widget-link {\n position: relative;\n border-bottom: none;\n}\n\n.tb-home-widget-link:hover {\n border-bottom: none;\n}\n\n.tb-home-widget-link:focus {\n border-bottom: none;\n}\n\n.tb-home-widget-link::after {\n content: 'arrow_forward';\n display: inline-block;\n transform: rotate(315deg);\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n font-size: 18px;\n color: rgba(0, 0, 0, 0.12);\n vertical-align: bottom;\n margin-left: 6px; \n}\n\n.tb-home-widget-link:hover::after {\n color: inherit;\n}\n\n.tb-home-widget-info-icon {\n color: rgba(0, 0, 0, 0.12);\n font-size: 16px;\n width: 16px;\n height: 16px;\n line-height: 15px;\n vertical-align: middle;\n}\n\n.tb-widget-container > .tb-widget .tb-timewindow {\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n color: rgba(0, 0, 0, 0.54);\n padding: 0;\n}\n\n.tb-widget-container > .tb-widget .tb-legend-keys .tb-legend-label {\n cursor: pointer;\n user-select: none;\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n color: rgba(0, 0, 0, 0.54);\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .tb-widget-container > .tb-widget {\n border-radius: 4px;\n }\n .tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 2px !important;\n }\n .tb-hide-md {\n display: none;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 8px !important;\n }\n .tb-hide-lg {\n display: none;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-hide-md-lg {\n display: none;\n }\n\n .tb-home-widget-title {\n font-size: 12px;\n line-height: 16px;\n }\n \n .tb-widget-container > .tb-widget .tb-widget-title {\n padding: 0;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow {\n font-size: 12px;\n line-height: 16px;\n min-height: 24px;\n padding: 0;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow .mat-mdc-icon-button.tb-mat-32 {\n width: 24px;\n height: 24px;\n line-height: 24px;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow .mat-mdc-icon-button.tb-mat-32 .mat-icon {\n width: 18px;\n height: 18px;\n font-size: 18px;\n }\n \n .tb-widget-container > .tb-widget tb-legend {\n padding-bottom: 0 !important;\n }\n \n .tb-widget-container > .tb-widget .tb-legend-keys .tb-legend-label {\n font-size: 11px;\n line-height: 16px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (max-width: 959px), screen and (min-width: 1820px) {\n .tb-hide-not-md-lg {\n display: none;\n }\n}\n" } }, - "externalId": null, "name": "System Administrator Home Page" } \ No newline at end of file diff --git a/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json b/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json index 37e8d82e45..063cb0def2 100644 --- a/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json +++ b/ui-ngx/src/assets/dashboard/tenant_admin_home_page.json @@ -69,142 +69,6 @@ "id": "d70cc256-4c7b-ee06-9905-b8c5e546605f", "typeFullFqn": "system.cards.markdown_card" }, - "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", - "filterId": null, - "dataKeys": [ - { - "name": "transportMsgCountHourly", - "type": "timeseries", - "label": "{i18n:widgets.transport-messages.title}", - "color": "#305680", - "settings": {}, - "_hash": 0.2880464219129071, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ], - "latestDataKeys": [] - } - ], - "timewindow": { - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, - "hideAggregation": true, - "hideAggInterval": false, - "hideTimezone": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 86400000, - "fixedTimewindow": { - "startTimeMs": 1680443065451, - "endTimeMs": 1680529465451 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "SUM", - "limit": 50000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "0px", - "settings": { - "stack": true, - "fontSize": 10, - "fontColor": "#545454", - "showTooltip": true, - "tooltipIndividual": false, - "tooltipCumulative": false, - "hideZeros": false, - "grid": { - "verticalLines": false, - "horizontalLines": false, - "outlineWidth": 0, - "color": "#545454", - "backgroundColor": null, - "tickColor": "#DDDDDD" - }, - "xaxis": { - "title": null, - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "min": 0, - "max": null, - "title": null, - "showLabels": true, - "color": "#545454", - "tickSize": null, - "tickDecimals": 0, - "ticksFormatter": "return value % 1000 === 0 ? ((value / 1000) + 'k') : '';" - }, - "defaultBarWidth": 1800000, - "barAlignment": "left", - "comparisonEnabled": false, - "timeForComparison": "previousInterval", - "comparisonCustomIntervalValue": 7200000, - "xaxisSecond": { - "axisPosition": "top", - "title": null, - "showLabels": true - }, - "customLegendEnabled": false, - "dataKeysListForLabels": [], - "showLegend": false, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": true, - "showTotal": false, - "showLatest": false - } - }, - "title": "Transport messages", - "dropShadow": false, - "enableFullscreen": false, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "widgetStyle": { - "padding": "0" - }, - "useDashboardTimewindow": false, - "actions": {}, - "displayTimewindow": true, - "showTitleIcon": false, - "titleTooltip": "", - "widgetCss": ".tb-widget-container > .tb-widget {\n border: none !important;\n border-radius: 0 !important;\n box-shadow: none !important;\n}\n\n.tb-widget-container > .tb-widget .flot-base {\n opacity: 0.48;\n}\n", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e", - "typeFullFqn": "system.charts.timeseries_bars_flot" - }, "867f82cf-ecf2-2d5c-35cb-08c6f2edc3a4": { "type": "static", "sizeX": 7.5, @@ -269,159 +133,6 @@ "id": "a23185ad-dc46-806c-0e50-5b21fb080ace", "typeFullFqn": "system.home_page_widgets.getting_started" }, - "d26e5cd7-75ef-d475-00c7-1a2d1114efe8": { - "type": "timeseries", - "sizeX": 8, - "sizeY": 5, - "config": { - "datasources": [ - { - "type": "entity", - "name": null, - "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", - "filterId": null, - "dataKeys": [ - { - "name": "activeDevicesCountHourly", - "type": "timeseries", - "label": "{i18n:device.devices}", - "color": "#305680", - "settings": { - "hideDataByDefault": false, - "disableDataHiding": false, - "removeFromLegend": false, - "excludeFromStacking": false, - "showLines": true, - "lineWidth": 3, - "fillLines": true, - "fillLinesOpacity": 0.04, - "showPoints": false, - "showSeparateAxis": false, - "axisPosition": "left", - "comparisonSettings": { - "showValuesForComparison": true, - "comparisonValuesLabel": "", - "color": "" - }, - "thresholds": [] - }, - "_hash": 0.9688095820365725, - "aggregationType": null, - "units": null, - "decimals": null, - "funcBody": null, - "usePostProcessing": null, - "postFuncBody": null - } - ], - "latestDataKeys": [] - } - ], - "timewindow": { - "hideInterval": false, - "hideLastInterval": false, - "hideQuickInterval": false, - "hideAggregation": true, - "hideAggInterval": true, - "hideTimezone": false, - "selectedTab": 1, - "history": { - "historyType": 0, - "timewindowMs": 2592000000, - "interval": 7200000, - "fixedTimewindow": { - "startTimeMs": 1681400576338, - "endTimeMs": 1681486976338 - }, - "quickInterval": "CURRENT_DAY" - }, - "aggregation": { - "type": "NONE", - "limit": 25000 - } - }, - "showTitle": false, - "backgroundColor": "#fff", - "color": "rgba(0, 0, 0, 0.87)", - "padding": "0px", - "settings": { - "stack": false, - "fontSize": 10, - "fontColor": "#545454", - "showTooltip": true, - "tooltipIndividual": false, - "tooltipCumulative": false, - "hideZeros": false, - "grid": { - "verticalLines": false, - "horizontalLines": false, - "outlineWidth": 0, - "color": "#545454", - "backgroundColor": null, - "tickColor": "#DDDDDD" - }, - "xaxis": { - "title": null, - "showLabels": true, - "color": "#545454" - }, - "yaxis": { - "min": null, - "max": null, - "title": null, - "showLabels": true, - "color": "#545454", - "tickSize": null, - "tickDecimals": 0, - "ticksFormatter": "" - }, - "shadowSize": 0, - "smoothLines": true, - "comparisonEnabled": false, - "timeForComparison": "previousInterval", - "comparisonCustomIntervalValue": 7200000, - "xaxisSecond": { - "axisPosition": "top", - "title": null, - "showLabels": true - }, - "customLegendEnabled": false, - "dataKeysListForLabels": [], - "showLegend": false, - "legendConfig": { - "direction": "column", - "position": "bottom", - "sortDataKeys": false, - "showMin": false, - "showMax": false, - "showAvg": true, - "showTotal": false, - "showLatest": false - } - }, - "title": "Devices activity", - "dropShadow": false, - "enableFullscreen": false, - "titleStyle": { - "fontSize": "16px", - "fontWeight": 400 - }, - "useDashboardTimewindow": false, - "displayTimewindow": true, - "showTitleIcon": false, - "titleTooltip": "", - "widgetStyle": { - "padding": "0" - }, - "widgetCss": ".tb-widget-container > .tb-widget {\n border: none !important;\n border-radius: 0 !important;\n box-shadow: none !important;\n}\n", - "pageSize": 1024, - "noDataDisplayMessage": "" - }, - "row": 0, - "col": 0, - "id": "d26e5cd7-75ef-d475-00c7-1a2d1114efe8", - "typeFullFqn": "system.charts.basic_timeseries" - }, "ebbd0a6e-8a47-e770-5086-7f4974250f2d": { "type": "static", "sizeX": 7.5, @@ -777,6 +488,655 @@ "col": 0, "id": "7ac20b6a-dc40-b18e-9f5f-bca20bc693bb", "typeFullFqn": "system.cards.markdown_card" + }, + "bf7f6efa-7614-3bc0-c4d0-6665d67510a8": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": "", + "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "dataKeys": [ + { + "name": "transportMsgCountHourly", + "type": "timeseries", + "label": "{i18n:widgets.transport-messages.title}", + "color": "#305680", + "settings": { + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "bar", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "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)", + "pointShape": "emptyCircle", + "pointSize": 4, + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.39181957822569946, + "decimals": 0 + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + }, + "latestDataKeys": [] + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": true, + "hideAggInterval": false, + "hideTimezone": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000, + "fixedTimewindow": { + "startTimeMs": 1709807941444, + "endTimeMs": 1709894341444 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "SUM", + "limit": 25000 + }, + "timezone": null + }, + "showTitle": false, + "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": "return value === 0 ? value : ((value / 1000) + 'k');", + "showTicks": false, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": false, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": false, + "splitLinesColor": "rgba(0, 0, 0, 0.12)", + "id": "default", + "order": 0, + "min": 0, + "max": null + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "showTicks": false, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": false, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": false, + "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": false, + "legendLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendConfig": { + "direction": "column", + "position": "top", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "MMMM dd, yyyy", + "lastUpdateAgo": false, + "custom": true + }, + "tooltipDateFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipDateColor": "rgba(0, 0, 0, 0.76)", + "tooltipDateInterval": false, + "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", + "tooltipBackgroundBlur": 4, + "animation": { + "animation": true, + "animationThreshold": 2000, + "animationDuration": 1000, + "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": "0" + }, + "title": "Time series chart", + "dropShadow": false, + "enableFullscreen": false, + "titleStyle": 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": ".tb-widget-container > .tb-widget {\n border: none !important;\n border-radius: 0 !important;\n box-shadow: none !important;\n}\n\n.tb-time-series-chart-panel div.tb-widget-title {\n padding-top: 5px;\n padding-left: 5px;\n}", + "pageSize": 1024, + "units": "", + "decimals": null, + "noDataDisplayMessage": "", + "timewindowStyle": { + "showIcon": true, + "iconSize": "24px", + "icon": "query_builder", + "iconPosition": "left", + "font": { + "size": 14, + "sizeUnit": "px", + "family": "Roboto", + "weight": "400", + "style": "normal", + "lineHeight": "16px" + }, + "color": "rgba(0, 0, 0, 0.54)", + "displayTypePrefix": true + }, + "margin": "0px", + "borderRadius": "0px", + "iconSize": "0px" + }, + "row": 0, + "col": 0, + "id": "bf7f6efa-7614-3bc0-c4d0-6665d67510a8" + }, + "8e71a398-caf5-540d-cec5-6e5dc264343e": { + "typeFullFqn": "system.time_series_chart", + "type": "timeseries", + "sizeX": 8, + "sizeY": 5, + "config": { + "datasources": [ + { + "type": "entity", + "name": "", + "entityAliasId": "d9229b29-3f46-de8d-7fe8-eb0c43c75079", + "dataKeys": [ + { + "name": "activeDevicesCountHourly", + "type": "timeseries", + "label": "{i18n:device.devices}", + "color": "#305680", + "settings": { + "showInLegend": true, + "dataHiddenByDefault": false, + "type": "line", + "lineSettings": { + "showLine": true, + "step": false, + "stepType": "start", + "smooth": true, + "lineType": "solid", + "lineWidth": 3, + "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)", + "pointShape": "circle", + "pointSize": 14, + "fillAreaSettings": { + "type": "opacity", + "opacity": 0.04, + "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)", + "backgroundSettings": { + "type": "none", + "opacity": 0.4, + "gradient": { + "start": 100, + "end": 0 + } + } + } + }, + "_hash": 0.39181957822569946, + "decimals": 0, + "aggregationType": null, + "funcBody": null, + "usePostProcessing": null, + "postFuncBody": null + } + ], + "alarmFilterConfig": { + "statusList": [ + "ACTIVE" + ] + }, + "latestDataKeys": [] + } + ], + "timewindow": { + "hideInterval": false, + "hideLastInterval": false, + "hideQuickInterval": false, + "hideAggregation": true, + "hideAggInterval": true, + "hideTimezone": false, + "selectedTab": 1, + "history": { + "historyType": 0, + "timewindowMs": 2592000000, + "interval": 86400000, + "fixedTimewindow": { + "startTimeMs": 1709807941444, + "endTimeMs": 1709894341444 + }, + "quickInterval": "CURRENT_DAY" + }, + "aggregation": { + "type": "NONE", + "limit": 25000 + }, + "timezone": null + }, + "showTitle": false, + "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": null, + "showTicks": false, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": false, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": false, + "splitLinesColor": "rgba(0, 0, 0, 0.12)", + "id": "default", + "order": 0, + "min": 0 + } + }, + "thresholds": [], + "dataZoom": false, + "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)", + "showTicks": false, + "ticksColor": "rgba(0, 0, 0, 0.54)", + "showLine": false, + "lineColor": "rgba(0, 0, 0, 0.54)", + "showSplitLines": false, + "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": false, + "legendLabelFont": { + "family": "Roboto", + "size": 12, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "legendLabelColor": "rgba(0, 0, 0, 0.76)", + "legendConfig": { + "direction": "column", + "position": "top", + "sortDataKeys": false, + "showMin": false, + "showMax": false, + "showAvg": true, + "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)", + "tooltipShowDate": true, + "tooltipDateFormat": { + "format": "MMMM dd, yyyy", + "lastUpdateAgo": false, + "custom": true + }, + "tooltipDateFont": { + "family": "Roboto", + "size": 11, + "sizeUnit": "px", + "style": "normal", + "weight": "400", + "lineHeight": "16px" + }, + "tooltipDateColor": "rgba(0, 0, 0, 0.76)", + "tooltipDateInterval": false, + "tooltipBackgroundColor": "rgba(255, 255, 255, 0.76)", + "tooltipBackgroundBlur": 4, + "animation": { + "animation": true, + "animationThreshold": 2000, + "animationDuration": 200, + "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": "0" + }, + "title": "Time series chart", + "dropShadow": false, + "enableFullscreen": false, + "titleStyle": 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": ".tb-widget-container > .tb-widget {\n border: none !important;\n border-radius: 0 !important;\n box-shadow: none !important;\n}\n\n.tb-time-series-chart-panel div.tb-widget-title {\n padding-top: 5px;\n padding-left: 5px;\n}", + "pageSize": 1024, + "units": "", + "decimals": null, + "noDataDisplayMessage": "", + "timewindowStyle": { + "showIcon": true, + "iconSize": "24px", + "icon": "query_builder", + "iconPosition": "left", + "font": { + "size": 14, + "sizeUnit": "px", + "family": "Roboto", + "weight": "400", + "style": "normal", + "lineHeight": "16px" + }, + "color": "rgba(0, 0, 0, 0.54)", + "displayTypePrefix": true + }, + "margin": "0px", + "borderRadius": "0px", + "iconSize": "0px" + }, + "row": 0, + "col": 0, + "id": "8e71a398-caf5-540d-cec5-6e5dc264343e" } }, "states": { @@ -868,7 +1228,7 @@ "layouts": { "main": { "widgets": { - "8ee72d43-678c-4e25-e9a8-7d4cfd7a5f8e": { + "bf7f6efa-7614-3bc0-c4d0-6665d67510a8": { "sizeX": 24, "sizeY": 11, "row": 0, @@ -895,7 +1255,7 @@ "layouts": { "main": { "widgets": { - "d26e5cd7-75ef-d475-00c7-1a2d1114efe8": { + "8e71a398-caf5-540d-cec5-6e5dc264343e": { "sizeX": 24, "sizeY": 11, "row": 0, @@ -1049,6 +1409,5 @@ "dashboardCss": ".tb-widget-container > .tb-widget {\n border: 1px solid rgba(0, 0, 0, 0.05);\n box-shadow: 0px 5px 16px rgba(0, 0, 0, 0.04);\n border-radius: 12px;\n}\n\n.tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 16px !important;\n}\n\n.tb-card-title {\n display: grid;\n}\n\n.tb-home-widget-title {\n font-style: normal;\n font-weight: 500;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n color: rgba(0, 0, 0, 0.54);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.tb-home-widget-link {\n position: relative;\n border-bottom: none;\n}\n\n.tb-home-widget-link:hover {\n border-bottom: none;\n}\n\n.tb-home-widget-link:focus {\n border-bottom: none;\n}\n\n.tb-home-widget-link::after {\n content: 'arrow_forward';\n display: inline-block;\n transform: rotate(315deg);\n font-family: 'Material Icons';\n font-weight: normal;\n font-style: normal;\n font-size: 18px;\n color: rgba(0, 0, 0, 0.12);\n vertical-align: bottom;\n margin-left: 6px; \n}\n\n.tb-home-widget-link:hover::after {\n color: inherit;\n}\n\n.tb-home-widget-info-icon {\n color: rgba(0, 0, 0, 0.12);\n font-size: 16px;\n width: 16px;\n height: 16px;\n line-height: 15px;\n vertical-align: middle;\n}\n\n.tb-widget-container > .tb-widget .tb-timewindow {\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.25px;\n color: rgba(0, 0, 0, 0.54);\n padding: 0;\n}\n\n.tb-widget-container > .tb-widget .tb-legend-keys .tb-legend-label {\n cursor: pointer;\n user-select: none;\n font-weight: 400;\n font-size: 14px;\n line-height: 20px;\n letter-spacing: 0.2px;\n color: rgba(0, 0, 0, 0.54);\n}\n\n@media screen and (min-width: 960px) and (max-width: 1279px) {\n .tb-widget-container > .tb-widget {\n border-radius: 4px;\n }\n .tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 2px !important;\n }\n .tb-hide-md {\n display: none;\n }\n}\n\n@media screen and (min-width: 1280px) and (max-width: 1819px) {\n .tb-widget-container > .tb-widget:not([style*=\"padding: 0\"]) {\n padding: 8px !important;\n }\n .tb-hide-lg {\n display: none;\n }\n}\n\n@media screen and (min-width: 960px) and (max-width: 1819px) {\n .tb-hide-md-lg {\n display: none;\n }\n\n .tb-home-widget-title {\n font-size: 12px;\n line-height: 16px;\n }\n \n .tb-widget-container > .tb-widget .tb-widget-title {\n padding: 0;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow {\n font-size: 12px;\n line-height: 16px;\n min-height: 24px;\n padding: 0;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow .mat-mdc-icon-button.tb-mat-32 {\n width: 24px;\n height: 24px;\n line-height: 24px;\n }\n\n .tb-widget-container > .tb-widget .tb-timewindow .mat-mdc-icon-button.tb-mat-32 .mat-icon {\n width: 18px;\n height: 18px;\n font-size: 18px;\n }\n \n .tb-widget-container > .tb-widget tb-legend {\n padding-bottom: 0 !important;\n }\n \n .tb-widget-container > .tb-widget .tb-legend-keys .tb-legend-label {\n font-size: 11px;\n line-height: 16px;\n letter-spacing: 0.25px;\n }\n}\n\n@media screen and (max-width: 959px), screen and (min-width: 1820px) {\n .tb-hide-not-md-lg {\n display: none;\n }\n}\n" } }, - "externalId": null, "name": "Tenant Administrator Home Page" -} +} \ No newline at end of file diff --git a/ui-ngx/src/assets/help/en_US/notification/rate_limits.md b/ui-ngx/src/assets/help/en_US/notification/rate_limits.md index d314e681c2..c17610f39a 100644 --- a/ui-ngx/src/assets/help/en_US/notification/rate_limits.md +++ b/ui-ngx/src/assets/help/en_US/notification/rate_limits.md @@ -11,7 +11,7 @@ Available template parameters: * `api` - rate-limited API label; one of: 'REST API requests', 'REST API requests per customer', 'transport messages', 'transport messages per device', 'Cassandra queries', 'WS updates per session', 'notification requests', 'notification requests per rule', - 'entity version creation', 'entity version load', 'Edge events', 'Edge events per edge'; + 'entity version creation', 'entity version load', 'Edge events', 'Edge events per edge', 'Edge uplink messages', 'Edge uplink messages per edge'; * `limitLevelEntityType` - entity type of the limit level entity, e.g. 'Tenant', 'Device', 'Notification rule', 'Customer', etc.; * `limitLevelEntityId` - id of the limit level entity; * `limitLevelEntityName` - name of the limit level entity; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 687cdccad4..084754b05c 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -791,6 +791,9 @@ "api-usage": "API usage", "alarm": "Alarm", "alarms-created": "Alarms created", + "queue-stats": "Queue Stats", + "processing-failures-and-timeouts": "Processing Failures and Timeouts", + "exceptions": "Exceptions", "alarms-created-daily-activity": "Alarms created daily activity", "alarms-created-hourly-activity": "Alarms created hourly activity", "alarms-created-monthly-activity": "Alarms created monthly activity", @@ -861,7 +864,9 @@ "transport-messages-per-device": "Transport messages per device", "ws-updates-per-session": "WS updates per session", "edge-events": "Edge events", - "edge-events-per-edge": "Edge events per edge" + "edge-events-per-edge": "Edge events per edge", + "edge-uplink-messages": "Edge uplink messages", + "edge-uplink-messages-per-edge": "Edge uplink messages per edge" }, "audit-log": { "audit": "Audit", @@ -3173,6 +3178,11 @@ "avg": "avg", "total": "total", "latest": "latest", + "Min": "Min", + "Max": "Max", + "Avg": "Avg", + "Total": "Total", + "Latest": "Latest", "comparison-time-ago": { "previousInterval": "(previous interval)", "customInterval": "(custom interval)", @@ -3353,6 +3363,7 @@ "notification": "Notification", "notification-center": "Notification center", "notification-tap-action": "Notification tap action", + "notification-tap-action-hint": "If not enabled, the default alarm dashboard will be used", "notify": "notify", "notify-again": "Notify again", "notify-alarm-action": { @@ -4213,6 +4224,10 @@ "edit-edge-events-per-edge-rate-limit": "Edit edge events per edge rate limits", "edge-events-rate-limit": "Edge events", "edge-events-per-edge-rate-limit": "Edge events per edge", + "edit-edge-uplink-messages-rate-limit": "Edit edge uplink messages rate limits", + "edit-edge-uplink-messages-per-edge-rate-limit": "Edit edge uplink messages per edge rate limits", + "edge-uplink-messages-rate-limit": "Edge uplink messages", + "edge-uplink-messages-per-edge-rate-limit": "Edge uplink messages per edge", "messages-per": "messages per", "not-set": "Not set", "number-of-messages": "Number of messages", @@ -4333,8 +4348,13 @@ "preview": "Preview" }, "tooltip": { + "trigger": "Trigger", + "trigger-point": "Point", + "trigger-axis": "Axis", "value": "Value", "date": "Date", + "show-date-time-interval": "Show date time interval", + "show-date-time-interval-hint": "Show date time interval according to the data aggregation.", "background-color": "Background color", "background-blur": "Background blur" }, @@ -5200,6 +5220,7 @@ "card-buttons": "Card buttons", "show-card-buttons": "Show card buttons", "card-border-radius": "Card border radius", + "card-padding": "Card padding", "card-appearance": "Card appearance", "color": "Color", "tooltip": "Tooltip", @@ -6589,6 +6610,158 @@ "table-tabs": "Table tabs", "show-cell-actions-menu-mobile": "Show cell actions dropdown menu in mobile mode" }, + "time-series-chart": { + "chart": "Chart", + "chart-style": "Chart style", + "data-zoom": "Data zoom", + "stack-mode": "Stack mode", + "stack-mode-hint": "Stacks series on the chart. The series with the same unit would be put on top of each other.", + "axes": "Axes", + "y-axes": "Y axes", + "line-type": "Line type", + "line-type-solid": "Solid", + "line-type-dashed": "Dashed", + "line-type-dotted": "Dotted", + "line-width": "Line width", + "shape-empty-circle": "Empty circle", + "shape-circle": "Circle", + "shape-rect": "Rectangle", + "shape-round-rect": "Rounded rectangle", + "shape-triangle": "Triangle", + "shape-diamond": "Diamond", + "shape-pin": "Pin", + "shape-arrow": "Arrow", + "shape-none": "None", + "type-line": "Line", + "type-bar": "Bar", + "type-point": "Point", + "no-aggregation-bar-width-strategy": "Bar width strategy for non-aggregated data", + "no-aggregation-bar-width-strategy-group": "Group", + "no-aggregation-bar-width-strategy-separate": "Separate", + "bar-group-width": "Bar group width", + "bar-width": "Bar width", + "bar-width-relative": "Percentage of time window", + "bar-width-absolute": "Absolute (ms)", + "threshold": { + "thresholds": "Thresholds", + "source": "Source", + "key-value": "Key / Value", + "no-thresholds": "No thresholds configured", + "add-threshold": "Add threshold", + "type-constant": "Constant", + "type-latest-key": "Key", + "type-entity": "Entity", + "threshold-settings": "Threshold settings", + "remove-threshold": "Remove threshold", + "threshold-value-required": "Threshold value is required.", + "key-required": "Key is required.", + "entity-key-required": "Entity key is required.", + "line-appearance": "Line appearance", + "line-color": "Line color", + "start-symbol": "Start symbol", + "end-symbol": "End symbol", + "symbol-size": "size", + "label": "Label", + "label-position-start": "Start", + "label-position-middle": "Middle", + "label-position-end": "End", + "label-position-inside-start": "Inside start", + "label-position-inside-start-top": "Inside start top", + "label-position-inside-start-bottom": "Inside start bottom", + "label-position-inside-middle": "Inside middle", + "label-position-inside-middle-top": "Inside middle top", + "label-position-inside-middle-bottom": "Inside middle bottom", + "label-position-inside-end": "Inside end", + "label-position-inside-end-top": "Inside end top", + "label-position-inside-end-bottom": "Inside end bottom" + }, + "axis": { + "axes": "Axes", + "x-axis": "X axis", + "y-axis": "Y axis", + "y-axis-settings": "Y axis settings", + "remove-y-axis": "Remove Y axis", + "id": "Id", + "label": "Label", + "position": "Position", + "position-left": "Left", + "position-right": "Right", + "position-top": "Top", + "position-bottom": "Bottom", + "tick-labels": "Tick labels", + "ticks-formatter-function": "Ticks formatter function", + "show-ticks": "Show ticks", + "show-line": "Show line", + "show-split-lines": "Show split lines", + "show-split-lines-x-axis-hint": "If enabled, the vertical lines on the chart will be shown.", + "show-split-lines-y-axis-hint": "If enabled, the horizontal lines on the chart will be shown.", + "scale": "Scale", + "scale-min": "min", + "scale-max": "max", + "scale-auto": "Auto", + "min": "Min", + "max": "Max", + "show": "Show", + "add-y-axis": "Add Y axis" + }, + "animation": { + "animation": "Animation", + "animation-threshold": "Animation threshold", + "animation-duration": "Animation duration", + "animation-easing": "Animation easing", + "animation-delay": "Animation delay", + "update-animation-duration": "Update animation duration", + "update-animation-easing": "Update animation easing", + "update-animation-delay": "Update animation delay" + }, + "series": { + "legend-settings": "Legend settings", + "show-in-legend": "Show in legend", + "show-in-legend-hint": "Show series name and data in legend.", + "hidden-by-default": "Hidden by default", + "hidden-by-default-hint": "Make series hidden in legend by default.", + "series-type": "Series type", + "type": "Type", + "type-line": "Line", + "type-bar": "Bar", + "label-position-top": "Top", + "label-position-bottom": "Bottom", + "fill": "Fill", + "background": "Background", + "fill-type-none": "None", + "fill-type-solid": "Solid", + "fill-type-opacity": "Opacity", + "fill-type-gradient": "Gradient", + "opacity": "Opacity", + "gradient-stops": "Gradient stops", + "gradient-start": "start", + "gradient-end": "end", + "line": { + "line": "Line", + "show-line": "Show line", + "step-line": "Step line", + "step-type-start": "Start", + "step-type-middle": "Middle", + "step-type-end": "End", + "smooth-line": "Smooth line" + }, + "point": { + "points": "Points", + "show-points": "Show points", + "point-label": "Point label", + "point-label-hint": "Display label with value over the series point.", + "point-shape": "Point shape", + "point-size": "Point size" + }, + "bar": { + "show-border": "Show border", + "border-width": "Border width", + "border-radius": "Border radius", + "label": "Label", + "label-hint": "Display label with value over the bar." + } + } + }, "wind-speed-direction": { "layout": "Layout", "layout-default": "Default", diff --git a/ui-ngx/src/form.scss b/ui-ngx/src/form.scss index 524a4e86ac..e2390c795d 100644 --- a/ui-ngx/src/form.scss +++ b/ui-ngx/src/form.scss @@ -29,9 +29,11 @@ @media #{$breakpoint} { @include form-row-column; .mat-mdc-form-field, tb-unit-input { - width: auto; - &.medium-width { + &:not(.fixed-width) { width: auto; + &.medium-width { + width: auto; + } } } } @@ -55,6 +57,9 @@ &.no-padding-bottom { padding-bottom: 0; } + &.no-padding-top { + padding-top: 0; + } &.no-padding { padding: 0; } @@ -467,6 +472,17 @@ } } } + &.fixed-height { + .mat-mdc-form-field-infix { + max-height: 40px; + .mat-mdc-select { + max-height: 24px; + .mat-mdc-select-trigger { + max-height: 24px; + } + } + } + } } .tb-form-table { @@ -670,4 +686,14 @@ } } } + + .mat-mdc-option { + &.flex { + .mdc-list-item__primary-text { + display: flex; + align-items: center; + justify-content: flex-start; + } + } + } } diff --git a/ui-ngx/yarn.lock b/ui-ngx/yarn.lock index d78a9a52d6..4317883a2b 100644 --- a/ui-ngx/yarn.lock +++ b/ui-ngx/yarn.lock @@ -5367,13 +5367,13 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -echarts@^5.4.3: - version "5.4.3" - resolved "https://registry.yarnpkg.com/echarts/-/echarts-5.4.3.tgz#f5522ef24419164903eedcfd2b506c6fc91fb20c" - integrity sha512-mYKxLxhzy6zyTi/FaEbJMOZU1ULGEQHaeIeuMR5L+JnJTpz+YR03mnnpBhbR4+UYJAgiXgpyTVLffPAjOTLkZA== +echarts@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/echarts/-/echarts-5.5.0.tgz#c13945a7f3acdd67c134d8a9ac67e917830113ac" + integrity sha512-rNYnNCzqDAPCr4m/fqyUFv7fD9qIsd50S6GDFgO1DxZhncCsNsG7IfUlAlvZe5oSEQxtsjnHiUuppzccry93Xw== dependencies: tslib "2.3.0" - zrender "5.4.4" + zrender "5.5.0" editorconfig@^0.15.3: version "0.15.3" @@ -11101,9 +11101,9 @@ zone.js@~0.13.0: dependencies: tslib "^2.3.0" -zrender@5.4.4: - version "5.4.4" - resolved "https://registry.yarnpkg.com/zrender/-/zrender-5.4.4.tgz#8854f1d95ecc82cf8912f5a11f86657cb8c9e261" - integrity sha512-0VxCNJ7AGOMCWeHVyTrGzUgrK4asT4ml9PEkeGirAkKNYXYzoPJCLvmyfdoOXcjTHPs10OZVMfD1Rwg16AZyYw== +zrender@5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/zrender/-/zrender-5.5.0.tgz#54d0d6c4eda81a96d9f60a9cd74dc48ea026bc1e" + integrity sha512-O3MilSi/9mwoovx77m6ROZM7sXShR/O/JIanvzTwjN3FORfLSr81PsUGd7jlaYOeds9d8tw82oP44+3YucVo+w== dependencies: tslib "2.3.0"