From 6aed0e60c0fe4beda44a0d104a2b81da92e317eb Mon Sep 17 00:00:00 2001 From: Pankaj K Borade <62588059+BkPankaj@users.noreply.github.com> Date: Sat, 8 Jun 2024 15:31:18 +0530 Subject: [PATCH] Combined PR of ROS1 laser block and CI Workflow with basic frontend test (#309) * added ROS1 Laser block * added npm install yml code * changed node version * adding legacy in npm install * added backend installation * added CI workflow with basic frontend test * added permission for node modules * changed node version to 18 * modified node modules with cache * added opening google.com using selenium in standalone-chrome ~ first test * added automatic Opening VC and clicking File and Open button of menubar * Update collection-factory to include blocks in the UI --------- Co-authored-by: toshan-luktuke --- .github/first_test.py | 55 +++ .github/workflows/CI_Workflow.yml | 93 ++++ frontend/src/App.test.tsx | 4 +- .../blocks/collection/collection-factory.tsx | 6 +- .../collection/ros-sensors/ROSLaserScan.json | 396 ++++++++++++++++++ 5 files changed, 551 insertions(+), 3 deletions(-) create mode 100644 .github/first_test.py create mode 100644 .github/workflows/CI_Workflow.yml create mode 100644 frontend/src/components/blocks/collection/ros-sensors/ROSLaserScan.json diff --git a/.github/first_test.py b/.github/first_test.py new file mode 100644 index 00000000..e473bc00 --- /dev/null +++ b/.github/first_test.py @@ -0,0 +1,55 @@ +from selenium import webdriver +from selenium.webdriver.common.by import By +from selenium.webdriver.support.ui import WebDriverWait +from selenium.webdriver.support import expected_conditions as EC +import time + +try: + # Set up the webdriver to connect to the remote Selenium server + options = webdriver.ChromeOptions() + options.add_argument('--no-sandbox') + options.add_argument('--disable-dev-shm-usage') + + # Disable headless mode to show the Chrome UI + # options.add_argument('--headless=false') + + # Remote WebDriver URL (provided by the selenium/standalone-chrome service) + driver = webdriver.Remote( + command_executor='http://localhost:4444/wd/hub', + options=options + ) + + # Open the browser and go to the URL + driver.get('http://IP:4000') + + # time.sleep(120) + + + # Wait for the "File" button to be clickable and click it + basic_button = WebDriverWait(driver, 20).until( + EC.element_to_be_clickable((By.XPATH, "//button[contains(@class, 'menu-button') and .//span[text()='File']]")) + ) + basic_button.click() + + + # Wait for the dropdown menu to be visible + dropdown_menu = WebDriverWait(driver, 10).until( + EC.visibility_of_element_located((By.XPATH, "//ul[@role='menu' and @aria-label='File']")) + ) + + # Wait for the "Open" menu item to be clickable and click it + code_menu_item = WebDriverWait(driver, 20).until( + EC.element_to_be_clickable((By.XPATH, "//ul[@role='menu' and @aria-label='File']//li[text()='Open']")) + ) + code_menu_item.click() + + time.sleep(20) + + + # Capture a screenshot + driver.get_screenshot_as_file('screenshot.png') + +finally: + # Close the browser + if driver: + driver.quit() diff --git a/.github/workflows/CI_Workflow.yml b/.github/workflows/CI_Workflow.yml new file mode 100644 index 00000000..3ff45e6a --- /dev/null +++ b/.github/workflows/CI_Workflow.yml @@ -0,0 +1,93 @@ +name: CI Workflow + +on: + pull_request: + branches: + - 'master' + +jobs: + setup: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Install frontend dependencies + if: steps.cache-node-modules.outputs.cache-hit != 'true' + working-directory: frontend + run: npm ci --legacy-peer-deps + + - name: Cache Node modules + if: steps.cache-node-modules.outputs.cache-hit != 'true' + uses: actions/cache@v4 + with: + path: frontend/node_modules + key: ${{ runner.os }}-node-${{ hashFiles('**/frontend/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Create virtual environment + run: python -m venv .venv + + - name: Activate virtual environment + run: source .venv/bin/activate + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r backend/requirements.txt + + - name: Add .env file + run: cp backend/.env.template backend/.env + + - name: Generate static files + run: python backend/manage.py collectstatic + + - name: Save venv + uses: actions/upload-artifact@v4 + with: + name: venv + path: .venv + + frontend-tests: + runs-on: ubuntu-latest + needs: setup + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Restore Node modules cache + id: restore-node-modules + uses: actions/cache@v4 + with: + path: frontend/node_modules + key: ${{ runner.os }}-node-${{ hashFiles('**/frontend/package-lock.json') }} + + - name: Install frontend dependencies + if: steps.restore-node-modules.outputs.cache-hit != 'true' + working-directory: frontend + run: npm ci --legacy-peer-deps + + - name: Permissions for node_modules + run: chmod -R +x frontend/node_modules/.bin + + - name: Verify node_modules restoration + run: | + ls -la frontend/node_modules + ls -la frontend/node_modules/.bin + + - name: Run frontend test + run: npm test -- --watchAll=false + working-directory: frontend \ No newline at end of file diff --git a/frontend/src/App.test.tsx b/frontend/src/App.test.tsx index 2a68616d..e9d70bf6 100644 --- a/frontend/src/App.test.tsx +++ b/frontend/src/App.test.tsx @@ -2,8 +2,8 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; import App from './App'; -test('renders learn react link', () => { +test('renders App for test', () => { render(); - const linkElement = screen.getByText(/learn react/i); + const linkElement = screen.getByText(/File/i); expect(linkElement).toBeInTheDocument(); }); diff --git a/frontend/src/components/blocks/collection/collection-factory.tsx b/frontend/src/components/blocks/collection/collection-factory.tsx index 45104f6e..19b69676 100644 --- a/frontend/src/components/blocks/collection/collection-factory.tsx +++ b/frontend/src/components/blocks/collection/collection-factory.tsx @@ -109,7 +109,8 @@ export const collectionBlocks: { 'blocks': CollectionBlockType, 'children': { 'cameraRos': {'label': 'CameraROS'}, 'odometer': {'label': 'Odometer'}, - 'imu': {'label': 'IMU'} + 'imu': {'label': 'IMU'}, + 'laserRos':{'label':'LaserROS'} } }, 'ros2sensors': { @@ -170,12 +171,15 @@ export function getCollectionBlock(name: string) { return import('./ros-sensors/Odometer.json'); case 'drivers.rossensors.imu': return import('./ros-sensors/IMU.json'); + case 'drivers.rossensors.laserRos': + return import('./ros-sensors/ROSLaserScan.json'); case 'drivers.ros2sensors.cameraRos2': return import('./ros-sensors/ROS2Camera.json'); case 'drivers.ros2sensors.laserRos2': return import('./ros-sensors/ROS2LaserScan.json'); case 'processing.tensorflow.objectDetector': return import('./tensorflow/ObjectDetector.json'); + default: break; diff --git a/frontend/src/components/blocks/collection/ros-sensors/ROSLaserScan.json b/frontend/src/components/blocks/collection/ros-sensors/ROSLaserScan.json new file mode 100644 index 00000000..552fcf60 --- /dev/null +++ b/frontend/src/components/blocks/collection/ros-sensors/ROSLaserScan.json @@ -0,0 +1,396 @@ +{ + "editor": { + "id": "6168472f-f6d8-45cf-bc98-3051074065d5", + "locked": false, + "offsetX": -254.49013919915978, + "offsetY": -42.460178287148096, + "zoom": 86.87812499999998, + "gridSize": 0, + "layers": [ + { + "id": "4ed5b2c0-ea15-4800-aa27-5a6509869898", + "type": "diagram-links", + "isSvg": true, + "transformed": true, + "models": { + "89196a6e-d40b-4b8f-b499-96a43299657c": { + "id": "89196a6e-d40b-4b8f-b499-96a43299657c", + "type": "default", + "selected": false, + "source": "0005-248b709f-e6c6-4b8d-94f1-d986193b7cad", + "sourcePort": "8563511b-b3d0-4833-9426-a929f9d72a85", + "target": "0d952efb-e25e-41dc-8ee1-6ca00db1a831", + "targetPort": "7c302710-552a-4be3-aa87-47143ad3442e", + "points": [ + { + "id": "9cab4f63-8c44-4be8-ba68-8cdaefe623a2", + "type": "point", + "x": 983.8381516626423, + "y": 137.42643805645446 + }, + { + "id": "d47a6102-e816-4e13-90ef-c8b655d8e62f", + "type": "point", + "x": 1317.2937968058027, + "y": 363.1449356346666 + } + ], + "labels": [], + "width": 3, + "color": "gray", + "curvyness": 50, + "selectedColor": "rgb(0,192,255)" + }, + "2cfa2c28-af53-4daf-a364-162e74775831": { + "id": "2cfa2c28-af53-4daf-a364-162e74775831", + "type": "default", + "selected": false, + "source": "c9fda9bb-631f-45dd-9551-47f856966126", + "sourcePort": "03136f30-35dd-4713-bac2-afc19626ed88", + "target": "0d952efb-e25e-41dc-8ee1-6ca00db1a831", + "targetPort": "258fd7ac-4b13-468f-b5bf-5bfb84e6b1ba", + "points": [ + { + "id": "4855eb9e-572a-47d6-a99f-0a56107014b0", + "type": "point", + "x": 677.1633130776993, + "y": 497.3750864161133 + }, + { + "id": "47449955-47be-407c-850d-a9ac04bcd565", + "type": "point", + "x": 1018.8105124252947, + "y": 609.5437274946717 + } + ], + "labels": [], + "width": 3, + "color": "gray", + "curvyness": 50, + "selectedColor": "rgb(0,192,255)" + }, + "556ded65-b41d-41e5-a4ab-7b97400ab48c": { + "id": "556ded65-b41d-41e5-a4ab-7b97400ab48c", + "type": "default", + "selected": false, + "source": "0d952efb-e25e-41dc-8ee1-6ca00db1a831", + "sourcePort": "64c11722-3520-4eee-a740-b3311361d269", + "target": "d69e6f41-3d18-4e5d-b747-69dd613dd6ef", + "targetPort": "75040235-29aa-460e-9cb4-623ccd47a144", + "points": [ + { + "id": "d6b79658-7bae-44af-9555-a630f2860f9c", + "type": "point", + "x": 1615.7962341197708, + "y": 609.5437274946717 + }, + { + "id": "4d31b78b-7efb-4ddb-ba14-9bfc89d96476", + "type": "point", + "x": 1777.1141916432186, + "y": 520.1848690003728 + } + ], + "labels": [], + "width": 3, + "color": "gray", + "curvyness": 50, + "selectedColor": "rgb(0,192,255)" + } + } + }, + { + "id": "d392083a-f18f-44c0-92da-fd7de9ccfef9", + "type": "diagram-nodes", + "isSvg": false, + "transformed": true, + "models": { + "0d952efb-e25e-41dc-8ee1-6ca00db1a831": { + "id": "0d952efb-e25e-41dc-8ee1-6ca00db1a831", + "type": "basic.code", + "selected": false, + "x": 1010.4923703139029, + "y": 285.95298846293565, + "ports": [ + { + "id": "258fd7ac-4b13-468f-b5bf-5bfb84e6b1ba", + "type": "port.input", + "x": 1011.3095878857098, + "y": 602.0427941733658, + "name": "Enable", + "alignment": "left", + "parentNode": "0d952efb-e25e-41dc-8ee1-6ca00db1a831", + "links": [ + "2cfa2c28-af53-4daf-a364-162e74775831" + ], + "in": true, + "label": "Enable", + "hideLabel": false + }, + { + "id": "64c11722-3520-4eee-a740-b3311361d269", + "type": "port.output", + "x": 1608.295300798465, + "y": 602.0427941733658, + "name": "Out", + "alignment": "right", + "parentNode": "0d952efb-e25e-41dc-8ee1-6ca00db1a831", + "links": [ + "556ded65-b41d-41e5-a4ab-7b97400ab48c" + ], + "in": false, + "label": "Out", + "hideLabel": false + }, + { + "id": "7c302710-552a-4be3-aa87-47143ad3442e", + "type": "port.parameter", + "x": 1309.7928722662177, + "y": 355.6440023133607, + "name": "ROSTopic", + "alignment": "top", + "parentNode": "0d952efb-e25e-41dc-8ee1-6ca00db1a831", + "links": [ + "89196a6e-d40b-4b8f-b499-96a43299657c" + ], + "in": true, + "label": "ROSTopic", + "hideLabel": false + } + ], + "data": { + "code": "#!/usr/bin/env python\n\nimport rospy\nfrom sensor_msgs.msg import LaserScan\n\nmeasure = None\n\ndef callback(msg):\n global measure\n\n measure = []\n for i in range(len(msg.ranges)):\n measure.extend((str(msg.ranges[i]),))\n\ndef main(inputs, outputs, parameters, synchronise):\n global measure\n\n try:\n rospy.init_node('laser_subscriber', anonymous=True)\n topic = parameters.read_string(\"ROSTopic\")\n rospy.Subscriber(topic, LaserScan, callback)\n\n auto_enable = False\n try:\n enable = inputs.read_number(\"Enable\")\n except Exception:\n auto_enable = True\n\n while (auto_enable or inputs.read_number('Enable') and not rospy.is_shutdown():\n if measure is not None:\n outputs.share_array(\"Out\", measure)\n measure = None \n\n synchronise()\n\n finally:\n synchronise()\n", + "frequency": "30", + "params": [ + { + "name": "ROSTopic" + } + ], + "ports": { + "in": [ + { + "name": "Enable" + } + ], + "out": [ + { + "name": "Out" + } + ] + }, + "size": { + "width": "809px", + "height": "300px" + } + } + }, + "0005-248b709f-e6c6-4b8d-94f1-d986193b7cad": { + "id": "0005-248b709f-e6c6-4b8d-94f1-d986193b7cad", + "type": "basic.constant", + "selected": false, + "x": 923.0280066509573, + "y": 21.93498493324092, + "ports": [ + { + "id": "8563511b-b3d0-4833-9426-a929f9d72a85", + "type": "port.output", + "x": 976.3372271230573, + "y": 129.92551351686953, + "name": "constant-out", + "alignment": "bottom", + "parentNode": "0005-248b709f-e6c6-4b8d-94f1-d986193b7cad", + "links": [ + "89196a6e-d40b-4b8f-b499-96a43299657c" + ], + "in": false, + "label": "ROSTopic", + "hideLabel": true + } + ], + "data": { + "name": "ROSTopic", + "value": "/scan", + "local": true + } + }, + "c9fda9bb-631f-45dd-9551-47f856966126": { + "id": "c9fda9bb-631f-45dd-9551-47f856966126", + "type": "basic.input", + "selected": false, + "x": 583.8709677419363, + "y": 470.9677419354845, + "ports": [ + { + "id": "03136f30-35dd-4713-bac2-afc19626ed88", + "type": "port.output", + "x": 669.6623885381143, + "y": 489.8741530948074, + "name": "input-out", + "alignment": "right", + "parentNode": "c9fda9bb-631f-45dd-9551-47f856966126", + "links": [ + "2cfa2c28-af53-4daf-a364-162e74775831" + ], + "in": false, + "label": "Enable", + "hideLabel": true + } + ], + "data": { + "name": "Enable" + } + }, + "d69e6f41-3d18-4e5d-b747-69dd613dd6ef": { + "id": "d69e6f41-3d18-4e5d-b747-69dd613dd6ef", + "type": "basic.output", + "selected": false, + "x": 1768.8229478865371, + "y": 493.7829458443716, + "ports": [ + { + "id": "75040235-29aa-460e-9cb4-623ccd47a144", + "type": "port.input", + "x": 1769.6132671036337, + "y": 512.683935679067, + "name": "output-in", + "alignment": "left", + "parentNode": "d69e6f41-3d18-4e5d-b747-69dd613dd6ef", + "links": [ + "556ded65-b41d-41e5-a4ab-7b97400ab48c" + ], + "in": true, + "label": "output-in", + "hideLabel": true + } + ], + "data": { + "name": "Out" + } + } + } + } + ] + }, + "version": "3.0", + "package": { + "name": "LaserROS1", + "version": "1.0", + "description": "Reads laser data and shares an array with its measures", + "author": "Pankaj Borade", + "image": "data:image/png;base64,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5EgBmv5CAXg/o2/KAQO3QGjN7a21j9MHIENdpW6Ag0NgpEjZ6x7vDnb29u+ZVn8/ZCNyoUf8XuoAAA0aaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/Pgo8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA0LjQuMC1FeGl2MiI+CiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIKICAgIHhtbG5zOnN0RXZ0PSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VFdmVudCMiCiAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgICB4bWxuczpHSU1QPSJodHRwOi8vd3d3LmdpbXAub3JnL3htcC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIKICAgeG1wTU06RG9jdW1lbnRJRD0iZ2ltcDpkb2NpZDpnaW1wOmVhOTc3ZDM5LTBkZDUtNGQzYi1hZTdjLWJmYzk0NjRhNDAzZSIKICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozZjM0MTU4Yi1iZDk2LTRhM2YtODM4NC01ZDljODgxYWEzYjEiCiAgIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo4YjRjNjE4YS1kYjg2LTQ1M2ItYmExZC00MzdjM2Q0ZDZlNTgiCiAgIGRjOkZvcm1hdD0iaW1hZ2UvcG5nIgogICBHSU1QOkFQST0iMi4wIgogICBHSU1QOlBsYXRmb3JtPSJMaW51eCIKICAgR0lNUDpUaW1lU3RhbXA9IjE2NjE1MTI3MjM4OTIxNjQiCiAgIEdJTVA6VmVyc2lvbj0iMi4xMC4zMCIKICAgdGlmZjpPcmllbnRhdGlvbj0iMSIKICAgeG1wOkNyZWF0b3JUb29sPSJHSU1QIDIuMTAiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJzYXZlZCIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iLyIKICAgICAgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo0YjZiMjllNi1iZTZhLTRhNzctYTI4ZC05MzMwNTEwZDcxODIiCiAgICAgIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkdpbXAgMi4xMCAoTGludXgpIgogICAgICBzdEV2dDp3aGVuPSIyMDIyLTA4LTI2VDEzOjE4OjQzKzAyOjAwIi8+CiAgICA8L3JkZjpTZXE+CiAgIDwveG1wTU06SGlzdG9yeT4KICA8L3JkZjpEZXNjcmlwdGlvbj4KIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAKPD94cGFja2V0IGVuZD0idyI/PjDYjdYAAAAGYktHRAAPAHoA/eF5T4kAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfmCBoLEivbAMh3AAAUpklEQVR42u2da4xc512Hn3duO7ter9d2nDhcmksztGqJqJp+KFKQoE0LKnJIUNRysUARhDRZCjiRQHIISZtSFBUlATGhTUApEERTQhLqConLBwulEgU5IFXhA5sbAhLHXtuzt9nZub18eM96Z845OzszO5cz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" + }, + "design": { + "board": "Python3-Noetic", + "graph": { + "blocks": [ + { + "id": "0d952efb-e25e-41dc-8ee1-6ca00db1a831", + "type": "basic.code", + "data": { + "code": "#!/usr/bin/env python\n\nimport rospy\nfrom sensor_msgs.msg import LaserScan\n\nmeasure = None\n\ndef callback(msg):\n global measure\n\n measure = []\n for i in range(len(msg.ranges)):\n measure.extend((str(msg.ranges[i]),))\n\ndef main(inputs, outputs, parameters, synchronise):\n global measure\n\n try:\n rospy.init_node('laser_subscriber', anonymous=True)\n topic = parameters.read_string(\"ROSTopic\")\n rospy.Subscriber(topic, LaserScan, callback)\n\n auto_enable = False\n try:\n enable = inputs.read_number(\"Enable\")\n except Exception:\n auto_enable = True\n\n while (auto_enable or inputs.read_number('Enable') and not rospy.is_shutdown()):\n if measure is not None:\n outputs.share_array(\"Out\", measure)\n measure = None \n\n synchronise()\n\n finally:\n synchronise()\n", + "frequency": "30", + "params": [ + { + "name": "ROSTopic" + } + ], + "ports": { + "in": [ + { + "name": "Enable" + } + ], + "out": [ + { + "name": "Out" + } + ] + }, + "size": { + "width": "809px", + "height": "300px" + } + }, + "position": { + "x": 1010.4923703139029, + "y": 285.95298846293565 + } + }, + { + "id": "0005-248b709f-e6c6-4b8d-94f1-d986193b7cad", + "type": "basic.constant", + "data": { + "name": "ROSTopic", + "value": "/scan", + "local": true + }, + "position": { + "x": 923.0280066509573, + "y": 21.93498493324092 + } + }, + { + "id": "c9fda9bb-631f-45dd-9551-47f856966126", + "type": "basic.input", + "data": { + "name": "Enable" + }, + "position": { + "x": 583.8709677419363, + "y": 470.9677419354845 + } + }, + { + "id": "d69e6f41-3d18-4e5d-b747-69dd613dd6ef", + "type": "basic.output", + "data": { + "name": "Out" + }, + "position": { + "x": 1768.8229478865371, + "y": 493.7829458443716 + } + } + ], + "wires": [ + { + "source": { + "block": "0005-248b709f-e6c6-4b8d-94f1-d986193b7cad", + "port": "constant-out", + "name": "ROSTopic" + }, + "target": { + "block": "0d952efb-e25e-41dc-8ee1-6ca00db1a831", + "port": "ROSTopic", + "name": "ROSTopic" + } + }, + { + "source": { + "block": "c9fda9bb-631f-45dd-9551-47f856966126", + "port": "input-out", + "name": "Enable" + }, + "target": { + "block": "0d952efb-e25e-41dc-8ee1-6ca00db1a831", + "port": "Enable", + "name": "Enable" + } + }, + { + "source": { + "block": "0d952efb-e25e-41dc-8ee1-6ca00db1a831", + "port": "Out", + "name": "Out" + }, + "target": { + "block": "d69e6f41-3d18-4e5d-b747-69dd613dd6ef", + "port": "output-in", + "name": "output-in" + } + } + ] + } + }, + "dependencies": {} +} \ No newline at end of file