diff --git a/snap/local/foxglove-sensors.json b/snap/local/foxglove-sensors.json index 3991553..317c0c0 100644 --- a/snap/local/foxglove-sensors.json +++ b/snap/local/foxglove-sensors.json @@ -1,452 +1,429 @@ { - "configById": { - "Plot!1u5bb0v": { - "paths": [ - { - "value": "/imu_broadcaster/imu.orientation.w", - "enabled": true, - "timestampMethod": "receiveTime" - }, - { - "value": "/imu_broadcaster/imu.orientation.x", - "enabled": true, - "timestampMethod": "receiveTime" - }, - { - "value": "/imu_broadcaster/imu.orientation.y", - "enabled": true, - "timestampMethod": "receiveTime" - }, - { - "value": "/imu_broadcaster/imu.orientation.z", - "enabled": true, - "timestampMethod": "receiveTime" - } + "configById": { + "Plot!1u5bb0v": { + "paths": [ + { + "value": "/imu_broadcaster/imu.orientation.w", + "enabled": true, + "timestampMethod": "receiveTime" + }, + { + "value": "/imu_broadcaster/imu.orientation.x", + "enabled": true, + "timestampMethod": "receiveTime" + }, + { + "value": "/imu_broadcaster/imu.orientation.y", + "enabled": true, + "timestampMethod": "receiveTime" + }, + { + "value": "/imu_broadcaster/imu.orientation.z", + "enabled": true, + "timestampMethod": "receiveTime" + } + ], + "minYValue": -1.1, + "maxYValue": 1.1, + "showXAxisLabels": true, + "showYAxisLabels": true, + "showLegend": false, + "legendDisplay": "floating", + "showPlotValuesInLegend": false, + "isSynced": true, + "xAxisVal": "timestamp", + "sidebarDimension": 240, + "foxglovePanelTitle": "Plot", + "followingViewWidth": 30 + }, + "RosOut!b0toow": { + "searchTerms": [], + "minLogLevel": 2 + }, + "Publish!3yfprcp": { + "topicName": "/cmd_vel", + "datatype": "geometry_msgs/msg/Twist", + "buttonText": "Motors Test", + "buttonTooltip": "", + "buttonColor": "#a85600", + "advancedView": false, + "value": "{\n \"linear\": {\n \"x\": 1.0,\n \"y\": 0,\n \"z\": 0\n },\n \"angular\": {\n \"x\": 0,\n \"y\": 0,\n \"z\": -0.5\n }\n}" + }, + "3D!4atcr8w": { + "cameraState": { + "perspective": true, + "distance": 4.078136514915201, + "phi": 0.27634279333612527, + "thetaOffset": 88.9293939734993, + "targetOffset": [ + 0.34677234754289576, + 0.03223816139348337, + -2.8618304713627643e-18 ], - "minYValue": -1.1, - "maxYValue": 1.1, - "showXAxisLabels": true, - "showYAxisLabels": true, - "showLegend": false, - "legendDisplay": "floating", - "showPlotValuesInLegend": false, - "isSynced": true, - "xAxisVal": "timestamp", - "sidebarDimension": 240, - "foxglovePanelTitle": "Plot", - "followingViewWidth": 30 - }, - "RosOut!b0toow": { - "searchTerms": [], - "minLogLevel": 2 - }, - "Publish!3yfprcp": { - "topicName": "/cmd_vel", - "datatype": "geometry_msgs/msg/Twist", - "buttonText": "Motors Test", - "buttonTooltip": "", - "buttonColor": "#a85600", - "advancedView": false, - "value": "{\n \"linear\": {\n \"x\": 1.0,\n \"y\": 0,\n \"z\": 0\n },\n \"angular\": {\n \"x\": 0,\n \"y\": 0,\n \"z\": -0.5\n }\n}" - }, - "Tab!2qhku9u": { - "activeTabIdx": 0, - "tabs": [ - { - "title": "IMU Plots", - "layout": { - "first": "Plot!1u5bb0v", - "second": "RosOut!b0toow", - "direction": "column" - } - }, - { - "title": "Production", - "layout": "Publish!3yfprcp" - } - ] + "target": [ + 0, + 0, + 0 + ], + "targetOrientation": [ + 0, + 0, + 0, + 1 + ], + "fovy": 45, + "near": 0.5, + "far": 5000 }, - "3D!40jejke": { - "cameraState": { - "perspective": true, - "distance": 2.319644425997722, - "phi": 0.5121483430677293, - "thetaOffset": 89.80364880252282, - "targetOffset": [ - 0.04217571585974451, - -0.03971452594915587, - 1.5648103050465003e-17 - ], - "target": [ - 0, - 0, - 0 - ], - "targetOrientation": [ - 0, - 0, - 0, - 1 - ], - "fovy": 45, - "near": 0.5, - "far": 5000 + "followMode": "follow-pose", + "scene": {}, + "transforms": { + "frame:antenna_link": { + "visible": false }, - "followMode": "follow-pose", - "scene": { - "transforms": { - "axisScale": 0.4499999999999996, - "labelSize": 0.013834613718625963 - }, - "enableStats": true, - "ignoreColladaUpAxis": false, - "syncCamera": false + "frame:antenna_connector_link": { + "visible": false }, - "transforms": { - "frame:base_link": { - "visible": false - }, - "frame:body_link": { - "visible": false - }, - "frame:cover_link": { - "visible": false - }, - "frame:imu_link": { - "visible": false - }, - "frame:camera_link": { - "visible": false - }, - "frame:fl_range": { - "visible": false - }, - "frame:fr_range": { - "visible": false - }, - "frame:rl_range": { - "visible": false - }, - "frame:rr_range": { - "visible": false - }, - "frame:fl_wheel_link": { - "visible": true - }, - "frame:fr_wheel_link": { - "visible": true - }, - "frame:rl_wheel_link": { - "visible": true - }, - "frame:rr_wheel_link": { - "visible": true - }, - "frame:slamtec_rplidar_a2_link": { - "visible": false - }, - "frame:laser": { - "visible": false - }, - "frame:orbbec_astra_link": { - "visible": false - }, - "frame:depth": { - "visible": false - }, - "frame:odom": { - "visible": false - } + "frame:body_link": { + "visible": false }, - "topics": { - "/scan_filtered": { - "visible": true, - "colorField": "range", - "colorMode": "flat", - "colorMap": "turbo", - "pointSize": 8, - "flatColor": "#f90000" - }, - "/robot_description": { - "visible": true - } + "frame:base_link": { + "visible": false }, - "layers": { - "b2c64820-a936-4f65-82b5-7a04ef902009": { - "visible": true, - "frameLocked": true, - "label": "Grid", - "instanceId": "b2c64820-a936-4f65-82b5-7a04ef902009", - "layerId": "foxglove.Grid", - "size": 10, - "divisions": 100, - "lineWidth": 1, - "color": "#248eff", - "position": [ - 0, - 0, - 0 - ], - "rotation": [ - 0, - 0, - 0 - ], - "order": 2 - }, - "13d2ba45-fe87-488a-9410-c3b1a51c75de": { - "visible": true, - "frameLocked": true, - "label": "URDF", - "instanceId": "13d2ba45-fe87-488a-9410-c3b1a51c75de", - "layerId": "foxglove.Urdf", - "sourceType": "url", - "url": "http://localhost:8080/rosbot_xl.urdf", - "filePath": "", - "parameter": "", - "topic": "", - "framePrefix": "", - "displayMode": "auto", - "fallbackColor": "#ffffff", - "order": 3 - } + "frame:cover_link": { + "visible": false }, - "publish": { - "type": "point", - "poseTopic": "/move_base_simple/goal", - "pointTopic": "/clicked_point", - "poseEstimateTopic": "/initialpose", - "poseEstimateXDeviation": 0.5, - "poseEstimateYDeviation": 0.5, - "poseEstimateThetaDeviation": 0.26179939 + "frame:imu_link": { + "visible": false }, - "foxglovePanelTitle": "Lidar View", - "imageMode": {} - }, - "Teleop!13emlz4": { - "topic": "/cmd_vel", - "publishRate": 10, - "upButton": { - "field": "linear-x", - "value": 0.19754204525471783 + "frame:camera_mount_mid_link": { + "visible": false }, - "downButton": { - "field": "linear-x", - "value": -0.20000000000000015 + "frame:camera_mount_bot_link": { + "visible": false }, - "leftButton": { - "field": "angular-z", - "value": 1 + "frame:camera_mount_top_link": { + "visible": false }, - "rightButton": { - "field": "angular-z", - "value": -1 + "frame:camera_mount_link": { + "visible": false }, - "foxglovePanelTitle": "Diff Drive" - }, - "Gauge!4jffafa": { - "path": "/battery_state.voltage", - "minValue": 9.8, - "maxValue": 12.6, - "colorMap": "red-yellow-green", - "colorMode": "colormap", - "gradient": [ - "#0000ff", - "#ff00ff" - ], - "reverse": false, - "foxglovePanelTitle": "Battery" - }, - "Indicator!11kizr9": { - "path": "/battery_state.voltage", - "style": "background", - "fallbackColor": "#000000", - "fallbackLabel": "Ok", - "rules": [ - { - "operator": "<", - "rawValue": "10.8", - "color": "#ff0000", - "label": "Plug charger!" - } - ], - "foxglovePanelTitle": "Plug Charger Info" - }, - "Plot!4dl4s92": { - "paths": [ - { - "value": "/battery_state.voltage", - "enabled": true, - "timestampMethod": "receiveTime" - } - ], - "minYValue": 9.482842712474614, - "maxYValue": 13, - "showXAxisLabels": true, - "showYAxisLabels": true, - "showLegend": true, - "legendDisplay": "floating", - "showPlotValuesInLegend": true, - "isSynced": true, - "xAxisVal": "timestamp", - "sidebarDimension": 240, - "foxglovePanelTitle": "Voltage", - "followingViewWidth": 60 - }, - "Gauge!43atfmu": { - "path": "/battery_state.current", - "minValue": -3, - "maxValue": 3, - "colorMap": "red-yellow-green", - "colorMode": "colormap", - "gradient": [ - "#0000ff", - "#ff00ff" - ], - "reverse": true, - "foxglovePanelTitle": "Battery Current" - }, - "Gauge!1t2ifzu": { - "path": "/battery_state.charge", - "minValue": 0, - "maxValue": 3, - "colorMap": "red-yellow-green", - "colorMode": "colormap", - "gradient": [ - "#0000ff", - "#ff00ff" - ], - "reverse": true, - "foxglovePanelTitle": "Battery Charger Current" + "frame:slamtec_rplidar_s1_link": { + "visible": false + }, + "frame:laser": { + "visible": false + }, + "frame:odom": { + "visible": false + }, + "frame:fl_wheel_link": { + "visible": false + }, + "frame:fr_wheel_link": { + "visible": false + }, + "frame:rl_wheel_link": { + "visible": false + }, + "frame:rr_wheel_link": { + "visible": false + } }, - "Plot!3gmoqow": { - "paths": [ - { - "value": "/battery_state.current", - "enabled": true, - "timestampMethod": "receiveTime" - }, - { - "value": "/battery_state.charge", - "enabled": true, - "timestampMethod": "receiveTime" - } - ], - "minYValue": -3, - "maxYValue": 3, - "showXAxisLabels": true, - "showYAxisLabels": true, - "showLegend": true, - "legendDisplay": "floating", - "showPlotValuesInLegend": true, - "isSynced": true, - "xAxisVal": "timestamp", - "sidebarDimension": 240, - "foxglovePanelTitle": "Current" + "topics": { + "/robot_description": { + "visible": true + }, + "/scan": { + "visible": false, + "colorField": "intensity", + "colorMode": "colormap", + "colorMap": "turbo", + "pointSize": 6 + }, + "/scan_filtered": { + "visible": true, + "colorField": "intensity", + "colorMode": "colormap", + "colorMap": "turbo", + "pointSize": 6 + } }, - "Image!2zw8l2a": { - "cameraState": { - "distance": 20, - "perspective": true, - "phi": 60, - "target": [ + "layers": { + "eebc5132-8e96-40f6-a746-e61c0daaedab": { + "visible": true, + "frameLocked": true, + "label": "Grid", + "instanceId": "eebc5132-8e96-40f6-a746-e61c0daaedab", + "layerId": "foxglove.Grid", + "size": 10, + "divisions": 10, + "lineWidth": 1, + "color": "#248eff", + "position": [ 0, 0, 0 ], - "targetOffset": [ + "rotation": [ 0, 0, 0 ], - "targetOrientation": [ - 0, - 0, - 0, - 1 - ], - "thetaOffset": 45, - "fovy": 45, - "near": 0.5, - "far": 5000 - }, - "followMode": "follow-pose", - "scene": {}, - "transforms": {}, - "topics": {}, - "layers": {}, - "publish": { - "type": "point", - "poseTopic": "/move_base_simple/goal", - "pointTopic": "/clicked_point", - "poseEstimateTopic": "/initialpose", - "poseEstimateXDeviation": 0.5, - "poseEstimateYDeviation": 0.5, - "poseEstimateThetaDeviation": 0.26179939 - }, - "imageMode": { - "imageTopic": "/oak/rgb/image_raw/compressed", - "calibrationTopic": "/oak/rgb/camera_info" + "order": 1 } - } + }, + "publish": { + "type": "point", + "poseTopic": "/move_base_simple/goal", + "pointTopic": "/clicked_point", + "poseEstimateTopic": "/initialpose", + "poseEstimateXDeviation": 0.5, + "poseEstimateYDeviation": 0.5, + "poseEstimateThetaDeviation": 0.26179939 + }, + "imageMode": {} }, - "globalVariables": { - "globalVariable": 7 + "Tab!2qhku9u": { + "activeTabIdx": 0, + "tabs": [ + { + "title": "IMU Plots", + "layout": { + "first": "Plot!1u5bb0v", + "second": "RosOut!b0toow", + "direction": "column" + } + }, + { + "title": "Production", + "layout": "Publish!3yfprcp" + } + ] }, - "userNodes": { - "f5206e1d-deee-4f90-a03e-f561fbb9a7dd": { - "sourceCode": "// The ./types module provides helper types for your Input events and messages.\nimport { Input, Message } from \"./types\";\n\n// Your script can output well-known message types, any of your custom message types, or\n// complete custom message types.\n//\n// Use `Message` to access your data source types or well-known types:\n// type Twist = Message<\"geometry_msgs/Twist\">;\n//\n// Conventionally, it's common to make a _type alias_ for your script's output type\n// and use that type name as the return type for your script function.\n// Here we've called the type `Output` but you can pick any type name.\ntype Output = {\n hello: string;\n};\n\n// These are the topics your script \"subscribes\" to. Studio will invoke your script function\n// when any message is received on one of these topics.\nexport const inputs = [\"/input/topic\"];\n\n// Any output your script produces is \"published\" to this topic. Published messages are only visible within Studio, not to your original data source.\nexport const output = \"/studio_script/output_topic\";\n\n// This function is called with messages from your input topics.\n// The first argument is an event with the topic, receive time, and message.\n// Use the `Input<...>` helper to get the correct event type for your input topic messages.\nexport default function script(event: Input<\"/input/topic\">): Output {\n return {\n hello: \"world!\",\n };\n};", - "name": "f5206e1d" + "Image!2zw8l2a": { + "cameraState": { + "distance": 20, + "perspective": true, + "phi": 60, + "target": [ + 0, + 0, + 0 + ], + "targetOffset": [ + 0, + 0, + 0 + ], + "targetOrientation": [ + 0, + 0, + 0, + 1 + ], + "thetaOffset": 45, + "fovy": 45, + "near": 0.5, + "far": 5000 + }, + "followMode": "follow-pose", + "scene": {}, + "transforms": {}, + "topics": {}, + "layers": {}, + "publish": { + "type": "point", + "poseTopic": "/move_base_simple/goal", + "pointTopic": "/clicked_point", + "poseEstimateTopic": "/initialpose", + "poseEstimateXDeviation": 0.5, + "poseEstimateYDeviation": 0.5, + "poseEstimateThetaDeviation": 0.26179939 }, - "1a9e6183-d4b1-47dd-a024-efc14ab90b6b": { - "sourceCode": "// This example shows how to subscribe to multiple input topics.\n//\n// NOTE:\n// User Scripts can subscribe to multiple input topics, but can only publish on a single topic.\n\nimport { Input } from \"./types\";\n\ntype Output = { topic: string };\ntype GlobalVariables = { id: number };\n\n// List all the input topics in the `input` array\nexport const inputs = [\"/input/topic\", \"/input/another\"];\nexport const output = \"/studio_script/output_topic\";\n\n// Make an InputEvent type alias. Since our node will get a message from either input topic, we need to enumerate the topics.\ntype InputEvent = Input<\"/input/topic\"> | Input<\"/input/another\">;\n\nexport default function node(event: InputEvent, globalVars: GlobalVariables): Output {\n // Remember that your node will get messages on each topic, so you\n // need to check each event's topic to know which fields are available on the message.\n switch (event.topic) {\n case \"/input/topic\":\n // topic specific input logic\n // Our message fields are specific to our topic message\n break;\n case \"/input/another\":\n // another specific logic\n break;\n }\n\n // Nodes can only output one type of message regardless of the inputs\n // Here we echo back the input topic as an example.\n return {\n topic: event.topic,\n };\n};\n", - "name": "1a9e6183" + "imageMode": { + "imageTopic": "/oak/rgb/image_raw/compressed", + "calibrationTopic": "/oak/rgb/camera_info" } }, - "playbackConfig": { - "speed": 1 + "Teleop!13emlz4": { + "topic": "/cmd_vel", + "publishRate": 10, + "upButton": { + "field": "linear-x", + "value": 0.19754204525471783 + }, + "downButton": { + "field": "linear-x", + "value": -0.20000000000000015 + }, + "leftButton": { + "field": "angular-z", + "value": 1 + }, + "rightButton": { + "field": "angular-z", + "value": -1 + }, + "foxglovePanelTitle": "Diff Drive" }, - "layout": { - "first": { - "direction": "row", - "first": "Tab!2qhku9u", - "second": { - "first": "3D!40jejke", - "second": { - "first": "Image!2zw8l2a", - "second": "Teleop!13emlz4", - "direction": "column" - }, - "direction": "row", - "splitPercentage": 57.03330110757996 + "Gauge!4jffafa": { + "path": "/battery_state.voltage", + "minValue": 9.8, + "maxValue": 12.6, + "colorMap": "red-yellow-green", + "colorMode": "colormap", + "gradient": [ + "#0000ff", + "#ff00ff" + ], + "reverse": false, + "foxglovePanelTitle": "Battery" + }, + "Indicator!11kizr9": { + "path": "/battery_state.voltage", + "style": "background", + "fallbackColor": "#000000", + "fallbackLabel": "Ok", + "rules": [ + { + "operator": "<", + "rawValue": "10.8", + "color": "#ff0000", + "label": "Plug charger!" + } + ], + "foxglovePanelTitle": "Plug Charger Info" + }, + "Plot!4dl4s92": { + "paths": [ + { + "value": "/battery_state.voltage", + "enabled": true, + "timestampMethod": "receiveTime" + } + ], + "minYValue": 9.482842712474614, + "maxYValue": 13, + "showXAxisLabels": true, + "showYAxisLabels": true, + "showLegend": true, + "legendDisplay": "floating", + "showPlotValuesInLegend": true, + "isSynced": true, + "xAxisVal": "timestamp", + "sidebarDimension": 240, + "foxglovePanelTitle": "Voltage", + "followingViewWidth": 60 + }, + "Gauge!43atfmu": { + "path": "/battery_state.current", + "minValue": -3, + "maxValue": 3, + "colorMap": "red-yellow-green", + "colorMode": "colormap", + "gradient": [ + "#0000ff", + "#ff00ff" + ], + "reverse": true, + "foxglovePanelTitle": "Battery Current" + }, + "Gauge!1t2ifzu": { + "path": "/battery_state.charge", + "minValue": 0, + "maxValue": 3, + "colorMap": "red-yellow-green", + "colorMode": "colormap", + "gradient": [ + "#0000ff", + "#ff00ff" + ], + "reverse": true, + "foxglovePanelTitle": "Battery Charger Current" + }, + "Plot!3gmoqow": { + "paths": [ + { + "value": "/battery_state.current", + "enabled": true, + "timestampMethod": "receiveTime" }, - "splitPercentage": 30.5849582172702 - }, + { + "value": "/battery_state.charge", + "enabled": true, + "timestampMethod": "receiveTime" + } + ], + "minYValue": -3, + "maxYValue": 3, + "showXAxisLabels": true, + "showYAxisLabels": true, + "showLegend": true, + "legendDisplay": "floating", + "showPlotValuesInLegend": true, + "isSynced": true, + "xAxisVal": "timestamp", + "sidebarDimension": 240, + "foxglovePanelTitle": "Current" + } + }, + "globalVariables": { + "globalVariable": 7 + }, + "userNodes": { + "f5206e1d-deee-4f90-a03e-f561fbb9a7dd": { + "sourceCode": "// The ./types module provides helper types for your Input events and messages.\nimport { Input, Message } from \"./types\";\n\n// Your script can output well-known message types, any of your custom message types, or\n// complete custom message types.\n//\n// Use `Message` to access your data source types or well-known types:\n// type Twist = Message<\"geometry_msgs/Twist\">;\n//\n// Conventionally, it's common to make a _type alias_ for your script's output type\n// and use that type name as the return type for your script function.\n// Here we've called the type `Output` but you can pick any type name.\ntype Output = {\n hello: string;\n};\n\n// These are the topics your script \"subscribes\" to. Studio will invoke your script function\n// when any message is received on one of these topics.\nexport const inputs = [\"/input/topic\"];\n\n// Any output your script produces is \"published\" to this topic. Published messages are only visible within Studio, not to your original data source.\nexport const output = \"/studio_script/output_topic\";\n\n// This function is called with messages from your input topics.\n// The first argument is an event with the topic, receive time, and message.\n// Use the `Input<...>` helper to get the correct event type for your input topic messages.\nexport default function script(event: Input<\"/input/topic\">): Output {\n return {\n hello: \"world!\",\n };\n};", + "name": "f5206e1d" + }, + "1a9e6183-d4b1-47dd-a024-efc14ab90b6b": { + "sourceCode": "// This example shows how to subscribe to multiple input topics.\n//\n// NOTE:\n// User Scripts can subscribe to multiple input topics, but can only publish on a single topic.\n\nimport { Input } from \"./types\";\n\ntype Output = { topic: string };\ntype GlobalVariables = { id: number };\n\n// List all the input topics in the `input` array\nexport const inputs = [\"/input/topic\", \"/input/another\"];\nexport const output = \"/studio_script/output_topic\";\n\n// Make an InputEvent type alias. Since our node will get a message from either input topic, we need to enumerate the topics.\ntype InputEvent = Input<\"/input/topic\"> | Input<\"/input/another\">;\n\nexport default function node(event: InputEvent, globalVars: GlobalVariables): Output {\n // Remember that your node will get messages on each topic, so you\n // need to check each event's topic to know which fields are available on the message.\n switch (event.topic) {\n case \"/input/topic\":\n // topic specific input logic\n // Our message fields are specific to our topic message\n break;\n case \"/input/another\":\n // another specific logic\n break;\n }\n\n // Nodes can only output one type of message regardless of the inputs\n // Here we echo back the input topic as an example.\n return {\n topic: event.topic,\n };\n};\n", + "name": "1a9e6183" + } + }, + "playbackConfig": { + "speed": 1 + }, + "layout": { + "first": { + "direction": "row", + "first": "Tab!2qhku9u", "second": { - "first": { - "first": "Gauge!4jffafa", - "second": "Indicator!11kizr9", + "first": "3D!4atcr8w", + "second": { + "first": "Teleop!13emlz4", + "second": "Image!2zw8l2a", "direction": "column" }, + "direction": "row" + }, + "splitPercentage": 30.5849582172702 + }, + "second": { + "first": { + "first": "Gauge!4jffafa", + "second": "Indicator!11kizr9", + "direction": "column" + }, + "second": { + "first": "Plot!4dl4s92", "second": { - "first": "Plot!4dl4s92", - "second": { - "first": { - "first": "Gauge!43atfmu", - "second": "Gauge!1t2ifzu", - "direction": "column" - }, - "second": "Plot!3gmoqow", - "direction": "row", - "splitPercentage": 40.95744286095124 + "first": { + "first": "Gauge!43atfmu", + "second": "Gauge!1t2ifzu", + "direction": "column" }, + "second": "Plot!3gmoqow", "direction": "row", - "splitPercentage": 38.157892540045665 + "splitPercentage": 40.95744286095124 }, "direction": "row", - "splitPercentage": 18.12550403728454 + "splitPercentage": 38.157892540045665 }, - "direction": "column", - "splitPercentage": 67.44897959183673 - } - } \ No newline at end of file + "direction": "row", + "splitPercentage": 18.12550403728454 + }, + "direction": "column", + "splitPercentage": 67.44897959183673 + } +} \ No newline at end of file