├── src
├── .eslintrc
├── components
│ ├── ROS.test.js
│ ├── ROSContext.js
│ ├── examples
│ │ ├── ToggleConnect.js
│ │ ├── EchoTopic.js
│ │ └── SimpleEcho.mdx
│ └── ROS.js
├── index.js
└── styles.module.css
├── .eslintignore
├── doczrc.js
├── .editorconfig
├── .prettierrc
├── .gitignore
├── .eslintrc.js
├── .github
└── workflows
│ ├── build.yml
│ └── docz.yml
├── .eslintrc
├── README.md
└── package.json
/src/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "jest": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | build/
2 | dist/
3 | docs/
4 | node_modules/
5 | .snapshots/
6 | *.min.js
7 |
--------------------------------------------------------------------------------
/doczrc.js:
--------------------------------------------------------------------------------
1 | export default {
2 | dest: './docs/',
3 | base: '/react-ros/',
4 | title: 'react-ros',
5 | }
6 |
--------------------------------------------------------------------------------
/src/components/ROS.test.js:
--------------------------------------------------------------------------------
1 | import { ROS } from './ROS'
2 |
3 | describe('ROS', () => {
4 | it('is truthy', () => {
5 | expect(ROS).toBeTruthy()
6 | })
7 | })
8 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import { ROS, useROS } from './components/ROS'
2 | export { ROS, useROS };
3 |
4 | import { ROSContext, ROSProvider } from './components/ROSContext'
5 | export { ROSContext, ROSProvider }
6 |
--------------------------------------------------------------------------------
/src/styles.module.css:
--------------------------------------------------------------------------------
1 | /* add css module styles here (optional) */
2 |
3 | .test {
4 | margin: 2em;
5 | padding: 0.5em;
6 | border: 2px solid #000;
7 | font-size: 2em;
8 | text-align: center;
9 | }
10 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "jsxSingleQuote": true,
4 | "semi": false,
5 | "tabWidth": 2,
6 | "bracketSpacing": true,
7 | "jsxBracketSameLine": false,
8 | "arrowParens": "always",
9 | "trailingComma": "none"
10 | }
11 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # See https://help.github.com/ignore-files/ for more about ignoring files.
3 |
4 | # dependencies
5 | node_modules
6 |
7 | # builds
8 | build
9 | dist
10 | docs
11 | .docz
12 | .rpt2_cache
13 |
14 | # misc
15 | .DS_Store
16 | **log/
17 |
18 | #.env
19 | .env.local
20 | .env.development.local
21 | .env.test.local
22 | .env.production.local
23 | .vscode
24 |
25 | npm-debug.log*
26 | yarn-debug.log*
27 | yarn-error.log*
28 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "env": {
3 | "browser": true,
4 | "es6": true
5 | },
6 | "extends": [
7 | "eslint:recommended",
8 | "plugin:react/recommended"
9 | ],
10 | "globals": {
11 | "Atomics": "readonly",
12 | "SharedArrayBuffer": "readonly"
13 | },
14 | "parserOptions": {
15 | "ecmaFeatures": {
16 | "jsx": true
17 | },
18 | "ecmaVersion": 2018,
19 | "sourceType": "module"
20 | },
21 | "plugins": [
22 | "react"
23 | ],
24 | "rules": {
25 | }
26 | };
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: build & test
2 |
3 | on:
4 | push:
5 | branches-ignore:
6 | - main
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | strategy:
12 | matrix:
13 | node-version: [10.x, 12.x]
14 | steps:
15 | - name: Checkout code
16 | uses: actions/checkout@v2
17 | - name: Use Node.js ${{ matrix.node-version }}
18 | uses: actions/setup-node@v1
19 | with:
20 | node-version: ${{ matrix.node-version }}
21 | - run: npm install
22 | - run: npm run build --if-present
23 | - run: npm test
24 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": [
4 | "standard",
5 | "standard-react",
6 | "plugin:prettier/recommended",
7 | "prettier/standard",
8 | "prettier/react"
9 | ],
10 | "env": {
11 | "node": true
12 | },
13 | "parserOptions": {
14 | "ecmaVersion": 2020,
15 | "ecmaFeatures": {
16 | "legacyDecorators": true,
17 | "jsx": true
18 | }
19 | },
20 | "settings": {
21 | "react": {
22 | "version": "16"
23 | }
24 | },
25 | "rules": {
26 | "space-before-function-paren": 0,
27 | "react/prop-types": 0,
28 | "react/jsx-handler-names": 0,
29 | "react/jsx-fragments": 0,
30 | "react/no-unused-prop-types": 0,
31 | "import/export": 0
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/components/ROSContext.js:
--------------------------------------------------------------------------------
1 | import React, { createContext, useState } from 'react'
2 | import PropTypes from 'prop-types'
3 | import ROSLIB from 'roslib'
4 |
5 | const rosObj = {
6 | ROS: new ROSLIB.Ros(),
7 | url: "ws://localhost:9090",
8 | isConnected: false,
9 | ROSConfirmedConnected: false,
10 | autoconnect: false,
11 | topics: [],
12 | services:[],
13 | listeners: [],
14 | }
15 |
16 | const ROSContext = createContext([{}, () => {}]);
17 |
18 | const ROSProvider = (props) => {
19 | const [ ros, setROS ] = useState(rosObj);
20 | return (
21 |
22 | {props.children}
23 |
24 | );
25 | }
26 |
27 | ROSProvider.propTypes = {
28 | children: PropTypes.node.isRequired,
29 | }
30 |
31 | export { ROSContext, ROSProvider };
32 |
--------------------------------------------------------------------------------
/.github/workflows/docz.yml:
--------------------------------------------------------------------------------
1 | name: Update Docs
2 | on:
3 | push:
4 | branches: main
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Checkout repo
10 | uses: actions/checkout@v2
11 | - name: Setup Node.js
12 | uses: actions/setup-node@v1
13 | with:
14 | node-version: '12.x'
15 | - name: Setup Git
16 | run: |
17 | git config user.name $GITHUB_ACTOR
18 | git config user.email gh-actions-${GITHUB_ACTOR}@github.com
19 | git remote add gh-origin https://${GITHUB_ACTOR}:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git
20 | env:
21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
22 | - name: Install & Build the Docz
23 | run: npm run predeploy
24 | - name: Deploy Docz
25 | uses: JamesIves/github-pages-deploy-action@4.1.1
26 | with:
27 | branch: gh-pages
28 | folder: docs
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-ros
2 | Connect your website to your robot!
3 |
4 | [](https://www.npmjs.com/package/react-ros) [](https://standardjs.com)
5 |
6 | ## Add react-ros to existing npm project
7 |
8 | ```
9 | $ npm install react-ros
10 | ```
11 |
12 | ## Install react-ros as standalone dev
13 |
14 | ```
15 | $ npm install
16 | ```
17 |
18 | ## Usage
19 |
20 | To make your own components that use ROS, simply import `useROS` and place it inside the `ROS` component that provides the ROS environment.
21 |
22 | We've provided a [few working examples](https://flynneva.github.io/react-ros/examples/simple_echo/) to help get you started.
23 |
24 | ## Special Thanks
25 | This project builds on the amazing work already done by people working on various projects. Here are a few that are the inspiration behind this component library:
26 | - [ROS 2](https://github.com/ros)
27 | - [roslibjs](https://github.com/RobotWebTools/roslibjs)
28 | - [rclnodejs](https://github.com/RobotWebTools/rclnodejs)
29 | - [webviz from GM Cruise](https://github.com/)
30 | - [jviz from Michael Johnson](https://github.com/MJohnson459/jviz)
31 |
32 | ## License
33 |
34 | MIT © [flynneva](https://github.com/flynneva)
35 |
--------------------------------------------------------------------------------
/src/components/examples/ToggleConnect.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from 'react'
2 | import { useROS } from '../ROS'
3 |
4 | function ToggleConnect() {
5 | const { isConnected, topics, url, changeUrl, toggleConnection, toggleAutoconnect} = useROS();
6 |
7 | // Try replacing this with your own unique rosbridge webserver address
8 | const defaultURL = "ws://0.0.0.0:9090";
9 |
10 | // only runs once when ToggleConnect is first rendered (mounted)
11 | useEffect(() => {
12 | console.log('ToggleConnect is mounted!');
13 | if (url !== defaultURL) {
14 | changeUrl(defaultURL);
15 | }
16 |
17 | if (!isConnected) {
18 | toggleAutoconnect();
19 | }
20 | },[])
21 |
22 | // runs every time there is an update to any state/rerender
23 | useEffect(() => {
24 | //console.log('rerender ToggleConnect');
25 | })
26 |
27 | return (
28 |
29 |
30 | Simple connect: Toggle Connect
31 | ROS url input: changeUrl(event.target.value)} />
32 | ROS url to connect to: {url}
33 | Status of ROS: { isConnected ? "connected" : "not connected" }
34 | Topics detected:
35 | { topics.map((topic, i) =>
{topic.path} )}
36 |
37 |
38 | );
39 | }
40 |
41 | export default ToggleConnect;
42 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-ros",
3 | "version": "0.10.0",
4 | "description": "A React Component Library to help connect your website to your robot",
5 | "author": "flynneva",
6 | "license": "MIT",
7 | "homepage": "https://flynneva.github.io/react-ros/readme",
8 | "repository": "https://github.com/flynneva/react-ros",
9 | "bugs": "https://github.com/flynneva/react-ros/issues",
10 | "main": "dist/index.js",
11 | "module": "dist/index.modern.js",
12 | "source": "src/index.js",
13 | "engines": {
14 | "node": ">=10"
15 | },
16 | "scripts": {
17 | "build": "microbundle-crl --no-compress --format modern,cjs",
18 | "start": "microbundle-crl watch --no-compress --format modern,cjs",
19 | "prepare": "run-s build",
20 | "test": "run-s test:unit test:lint test:build",
21 | "test:build": "run-s build",
22 | "test:lint": "eslint .",
23 | "test:unit": "cross-env CI=1 react-scripts test --env=jsdom",
24 | "test:watch": "react-scripts test --env=jsdom",
25 | "predeploy": "npm install && npm run build && npm run docz:build",
26 | "deploy": "gh-pages -d docs/",
27 | "docz:dev": "docz dev",
28 | "docz:build": "docz build",
29 | "docz:serve": "docz build && docz serve"
30 | },
31 | "peerDependencies": {
32 | "react": "^16.0.0"
33 | },
34 | "devDependencies": {
35 | "cross-env": "^7.0.3",
36 | "eslint": "^6.8.0",
37 | "eslint-config-prettier": "^6.7.0",
38 | "eslint-config-standard": "^14.1.0",
39 | "eslint-config-standard-react": "^9.2.0",
40 | "eslint-plugin-import": "^2.24.1",
41 | "eslint-plugin-node": "^11.0.0",
42 | "eslint-plugin-prettier": "^3.4.1",
43 | "eslint-plugin-promise": "^4.3.1",
44 | "eslint-plugin-react": "^7.24.0",
45 | "eslint-plugin-standard": "^4.0.1",
46 | "gh-pages": "^2.2.0",
47 | "microbundle-crl": "^0.13.11",
48 | "npm-run-all": "^4.1.5",
49 | "prettier": "^2.3.2",
50 | "react": "^16.13.1",
51 | "react-dom": "^16.13.1",
52 | "react-scripts": "3.4.1",
53 | "uws": "^10.148.2"
54 | },
55 | "files": [
56 | "dist"
57 | ],
58 | "dependencies": {
59 | "acorn": "^8.4.1",
60 | "docz": "^2.3.1",
61 | "roslib": "^1.1.0"
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/components/examples/EchoTopic.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from 'react'
2 | import { useROS } from '../ROS'
3 |
4 | var listener = null;
5 |
6 | function EchoTopic() {
7 | const { createListener, topics } = useROS();
8 | const [ topic, setTopic ] = useState('/');
9 | const [ queue, setQueue ] = useState(0);
10 | const [ compression, setCompression ] = useState('none');
11 |
12 | useEffect(() => {
13 | handleTopic(topic);
14 | });
15 |
16 | const unsubscribe = () => {
17 | if (listener) {
18 | console.log("Unsubscribing");
19 | listener.unsubscribe();
20 | }
21 | }
22 |
23 | const handleTopic = (topicInput) => {
24 | if (topic !== topicInput) {
25 | setTopic(topicInput);
26 | unsubscribe();
27 | return;
28 | }
29 |
30 | unsubscribe();
31 | listener = null;
32 |
33 | for (var i in topics) {
34 | if (topics[i].path == topicInput) {
35 | listener = createListener( topics[i].path,
36 | topics[i].msgType,
37 | Number(queue),
38 | compression);
39 | break;
40 | }
41 | }
42 |
43 | if (listener) {
44 | console.log("Subscribing to messages...");
45 | listener.subscribe(handleMsg);
46 | } else {
47 | console.log("Topic '" + topic + "' not found...make sure to input the full topic path - including the leading '/'");
48 | }
49 | }
50 |
51 | const handleQueue = (queueInput) => {
52 | setQueue(queueInput);
53 | }
54 |
55 | const handleCompression = (compInput) => {
56 | setCompression(compInput);
57 | }
58 |
59 | const handleMsg = (msg) => {
60 | console.log(msg);
61 | }
62 |
63 | return (
64 |
65 | Message Queue Length: handleQueue(event.target.value)} />
66 | Compression: handleCompression(event.target.value)} />
67 | Topic to echo: handleTopic(event.target.value)} />
68 |
69 | );
70 | }
71 |
72 | export default EchoTopic;
73 |
--------------------------------------------------------------------------------
/src/components/examples/SimpleEcho.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | name: Echo Topic
3 | route: /examples/simple_echo/
4 | menu: Examples
5 | ---
6 |
7 | ## Getting Started
8 |
9 | Throughout these examples we will assume you've already gone through the process of installing [ROS](http://wiki.ros.org/noetic/Installation/Ubuntu) or [ROS2](https://index.ros.org/doc/ros2/Installation/Foxy/) and have a [ROS websocket server](https://github.com/RobotWebTools/rosbridge_suite) running and available to connect with.
10 |
11 | The first step in connecting ROS with a website will always be establishing a connection. With `react-ros`, we make this easy by providing a very simple and easy-to-use context provider called `useROS` to get you started.
12 |
13 | ## Connect to ROS
14 |
15 | Here is an example of how you can use the `useROS` component and connect your website to your robot! In this example we provide a `ToggleConnect` component that uses the `useROS` context provider to quickly and easily integrate ROS into any button or status indicator you could want to use.
16 |
17 | ```javascript
18 | import React from 'react'
19 | import { useROS } from '../ROS'
20 |
21 | function ToggleConnect() {
22 | const { isConnected, topics, url, changeUrl, toggleConnection } = useROS();
23 |
24 | return (
25 |
26 |
27 | Simple connect: Toggle Connect
28 | ROS url input: changeUrl(event.target.value)} />
29 | ROS url to connect to: {url}
30 | Status of ROS: { isConnected ? "connected" : "not connected" }
31 | Topics detected:
32 | { topics.map((topic, i) =>
{topic.path} )}
33 |
34 |
35 | );
36 | }
37 |
38 | export default ToggleConnect;
39 | ```
40 |
41 | ## ToggleConnect in use
42 | To use the simple `ToggleConnect` component we made above, wrap it in the provided `ROS` context provider component from `react-ros` and everything should just work. Go ahead and try it live below on your robot!
43 |
44 | Here is a simple echo example that prints out messages on a specific topic to the console:
45 |
46 | ```javascript
47 | // EchoTopic.js
48 | import React, { useState, useEffect } from 'react'
49 | import { useROS } from 'react-ros'
50 |
51 | var listener = null;
52 |
53 | function EchoTopic() {
54 | const { createListener, topics } = useROS();
55 | const [ topic, setTopic ] = useState('/');
56 | const [ queue, setQueue ] = useState(0);
57 | const [ compression, setCompression ] = useState('none');
58 |
59 | useEffect(() => {
60 | handleTopic(topic);
61 | });
62 |
63 | const unsubscribe = () => {
64 | if (listener) {
65 | console.log("Unsubscribing");
66 | listener.unsubscribe();
67 | }
68 | }
69 |
70 | const handleTopic = (topicInput) => {
71 | if (topic !== topicInput) {
72 | setTopic(topicInput);
73 | unsubscribe();
74 | return;
75 | }
76 |
77 | unsubscribe();
78 | listener = null;
79 |
80 | for (var i in topics) {
81 | if (topics[i].path == topicInput) {
82 | listener = createListener( topics[i].path,
83 | topics[i].msgType,
84 | Number(queue),
85 | compression);
86 | break;
87 | }
88 | }
89 |
90 | if (listener) {
91 | console.log("Subscribing to messages...");
92 | listener.subscribe(handleMsg);
93 | } else {
94 | console.log("Topic '" + topic + "' not found...make sure to input the full topic path - including the leading '/'");
95 | }
96 | }
97 |
98 | const handleQueue = (queueInput) => {
99 | setQueue(queueInput);
100 | }
101 |
102 | const handleCompression = (compInput) => {
103 | setCompression(compInput);
104 | }
105 |
106 | const handleMsg = (msg) => {
107 | console.log(msg);
108 | }
109 |
110 | return (
111 |
112 | Message Queue Length: handleQueue(event.target.value)} />
113 | Compression: handleCompression(event.target.value)} />
114 | Topic to echo: handleTopic(event.target.value)} />
115 |
116 | );
117 | }
118 |
119 | export default EchoTopic;
120 | ```
121 |
122 | And then we use this EchoTopic component within the ROS context provider like below.
123 |
124 | **Note:** not shown in the code below is `import { ROS } from 'react-ros'` and `import EchoTopic from './EchoTopic'` at the top of the file. This is needed in order to use the `ROS` context provider component and the `EchoTopic` component.
125 |
126 |
127 | import { Playground } from 'docz'
128 | import { ROS } from '../ROS'
129 |
130 | import ToggleConnect from './ToggleConnect'
131 | import EchoTopic from './EchoTopic'
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 | ## Subscribe to a topic
142 |
143 | Taking the toggle example even further - once connected, you should be able to subscribe to a topic and print out messages as they are received.
144 |
145 |
--------------------------------------------------------------------------------
/src/components/ROS.js:
--------------------------------------------------------------------------------
1 | import React, { useContext, useEffect } from 'react'
2 | import ROSLIB from 'roslib'
3 | import { ROSContext, ROSProvider } from './ROSContext'
4 | import PropTypes from 'prop-types'
5 |
6 | // ROS Hook that lets others use ROS websocket connection
7 | // returns some useful functions & values
8 | function useROS() {
9 | const [ros, setROS] = useContext(ROSContext);
10 |
11 | useEffect(() => {
12 | if (!ros.isConnected) {
13 | if(ros.autoconnect) {
14 | console.log('autoconnecting');
15 | handleConnect();
16 | }
17 | }
18 | })
19 |
20 | function checkConnection() {
21 | if (ros.ROS){
22 | if (ros.isConnected) {
23 | if (!ros.ROSConfirmedConnected && ros.ROS.isConnected) {
24 | setROS(ros => ({ ...ros, ROSConfirmedConnected: ros.ROS.isConnected }))
25 | console.log("Both react-ros and roslibjs have confirmed connection.")
26 | }
27 | // Once we have that "confirmation" we need to continously check for good connection
28 | else if (ros.ROSConfirmedConnected && !ros.ROS.isConnected) {
29 | setROS(ros => ({ ...ros, isConnected: false }));
30 | handleDisconnect();
31 | }
32 | else if (!ros.ROS.isConnected) {
33 | console.log("React-ros has confirmed the connection, roslibjs has not yet.")
34 | }
35 | }
36 | }
37 | else{
38 | console.log("Initial connection not established yet")
39 | }
40 | }
41 |
42 |
43 | function toggleConnection() {
44 | if (ros.isConnected) {
45 | handleDisconnect();
46 | } else if (!ros.isConnected) {
47 | handleConnect();
48 | }
49 | }
50 |
51 | function toggleAutoconnect() {
52 | if (ros.autoconnect) {
53 | setROS(ros => ({ ...ros, autoconnect: false }));
54 | } else if (!ros.autoconnect) {
55 | setROS(ros => ({ ...ros, autoconnect: true }));
56 | }
57 | }
58 |
59 | function changeUrl(new_url) {
60 | setROS(ros => ({ ...ros, url: new_url }));
61 | }
62 |
63 | function getTopics() {
64 | const topicsPromise = new Promise((resolve, reject) => {
65 | ros.ROS.getTopics((topics) => {
66 | const topicList = topics.topics.map((topicName, i) => {
67 | return {
68 | path: topicName,
69 | msgType: topics.types[i],
70 | type: "topic",
71 | }
72 | });
73 | resolve({
74 | topics: topicList
75 | });
76 | reject({
77 | topics: []
78 | });
79 | }, (message) => {
80 | console.error("Failed to get topic", message)
81 | ros.topics = []
82 | });
83 | });
84 | topicsPromise.then((topics) => setROS(ros => ({ ...ros, topics: topics.topics })));
85 | return ros.topics;
86 | }
87 |
88 | function getServices() {
89 | const servicesPromise = new Promise((resolve, reject) => {
90 | ros.ROS.getServices((services) => {
91 | const serviceList = services.map((serviceName) => {
92 | return {
93 | path: serviceName,
94 | type: "service",
95 | }
96 | });
97 | resolve({
98 | services: serviceList
99 | });
100 | reject({
101 | services: []
102 | });
103 | }, (message) => {
104 | console.error("Failed to get services", message)
105 | ros.services = []
106 | });
107 | });
108 | servicesPromise.then((services) => setROS(ros => ({ ...ros, services: services.services })));
109 | return ros.services;
110 | }
111 |
112 | function createListener(topic, msg_type, to_queue, compression_type) {
113 | var newListener = new ROSLIB.Topic({
114 | ros: ros.ROS,
115 | name: topic,
116 | messageType: msg_type,
117 | queue_length: to_queue,
118 | compression: compression_type,
119 | })
120 |
121 | for (var listener in ros.listeners) {
122 | if (newListener.name === ros.listeners[listener].name) {
123 | console.log('Listener already available in ros.listeners[' + listener + ']');
124 | return ros.listeners[listener];
125 | }
126 | }
127 | ros.listeners.push(newListener);
128 | console.log('Listener ' + newListener.name + ' created');
129 | return newListener;
130 | }
131 |
132 | const handleConnect = () => {
133 | try {
134 | ros.ROS.connect(ros.url)
135 | ros.ROS.on('connection', () => {
136 | // console.log(connect)
137 | setROS(ros => ({ ...ros, isConnected: true })); // seems to take awhile for the roslibjs library to report connected
138 | setROS(ros => ({ ...ros, ROSConfirmedConnected: false }));
139 | getTopics();
140 | getServices();
141 | })
142 |
143 | ros.ROS.on('error', (error) => { //gets a little annoying on the console, but probably ok for now
144 | console.log(error);
145 | })
146 | } catch (e) {
147 | console.log(e);
148 | }
149 | }
150 |
151 | const handleDisconnect = () => {
152 | try {
153 | ros.ROS.close();
154 | setROS(ros => ({ ...ros, isConnected: false }));
155 | setROS(ros => ({ ...ros, topics: [] }));
156 | setROS(ros => ({ ...ros, listeners: [] }));
157 | setROS(ros => ({ ...ros, ROSConfirmedConnected: false }));
158 | } catch (e) {
159 | console.log(e);
160 | }
161 | console.log('Disconnected');
162 | }
163 |
164 | const removeAllListeners = () => {
165 | for (var mlistener in ros.listeners) {
166 | ros.listeners[mlistener].removeAllListeners();
167 | }
168 | setROS(ros => ({ ...ros, listeners: [] }));
169 | }
170 |
171 | function removeListener(listener) {
172 | for (var mlistener in ros.listeners) {
173 | if (listener.name === ros.listeners[mlistener].name) {
174 | console.log('Listener: ' + listener.name + ' is removed')
175 | ros.listeners.splice(mlistener, 1)
176 | listener.removeAllListeners();
177 | return
178 | }
179 | }
180 | console.log('Listener: ' + listener + ' is not a listener')
181 | }
182 |
183 | return {
184 | toggleConnection,
185 | changeUrl,
186 | getTopics,
187 | getServices,
188 | createListener,
189 | toggleAutoconnect,
190 | removeAllListeners,
191 | removeListener,
192 | checkConnection,
193 | ros: ros.ROS,
194 | isConnected: ros.isConnected,
195 | autoconnect: ros.autoconnect,
196 | url: ros.url,
197 | topics: ros.topics,
198 | services: ros.services,
199 | listeners: ros.listeners,
200 | }
201 | }
202 |
203 | // ROS Component to manage ROS websocket connection and provide
204 | // it to children props
205 | function ROS(props) {
206 | return (
207 |
208 | {props.children}
209 |
210 | );
211 | }
212 |
213 | ROS.propTypes = {
214 | children: PropTypes.node.isRequired,
215 | }
216 |
217 | export { useROS, ROS };
218 |
--------------------------------------------------------------------------------