Skip to content

Commit c92f2d2

Browse files
Consolidated infra changes from RSS Demo (#122)
* Add robot browser logs * Add webrtc logs * Run server in pm2 to restart on crash * MaskButton should allow for changing num masks * Formatting * Made Auto-Continue automatically checked --------- Co-authored-by: Amal Nanavati <amaln@uw.edu> Co-authored-by: Amal Nanavati <amaln@cs.washington.edu>
1 parent 962c7bb commit c92f2d2

File tree

9 files changed

+77
-53
lines changed

9 files changed

+77
-53
lines changed

feedingwebapp/README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ The overall user flow for this robot can be seen below.
1313
## Dependencies
1414
- [Node.js](https://nodejs.org/en/download/package-manager)
1515
- [`serve` must be globally installed](https://create-react-app.dev/docs/deployment/) (ideally with `sudo`): `sudo npm install -g serve`
16+
- [`pm2` must be globally installed](https://pm2.keymetrics.io/docs/usage/quick-start/): `npm install pm2@latest -g`
1617

1718
## Getting Started in Computer
1819

@@ -23,7 +24,7 @@ The overall user flow for this robot can be seen below.
2324
4. Source the directory: `source install/setup.bash`
2425
5. Navigate to the web app folder: `cd feeding_web_interface/feedingwebapp`
2526
6. Install web app dependencies: `npm install --legacy-peer-deps`
26-
1. You may also have to run `npx playwright install`; you might be prompted to run that after `node --env-file=.env start_robot_browser.js`
27+
1. You may also have to run `npx playwright install`; you might be prompted to run that after `node start_robot_browser.js`
2728
* Consider checking out the Troubleshooting section if there are errors in this process.
2829
If your workspace has already been built, you should run `source install/setup.bash`. If this is your first time building your workspace, you should `source /opt/ros/humble/setup.bash` and then run `colcon build` followed by `source install/setup.bash`. For both of the above cases, you must be in the main directory of your workspace (e.g., `src` should be a subfolder).
2930

@@ -33,7 +34,7 @@ If your workspace has already been built, you should run `source install/setup.b
3334
- If you're not running the robot code alongside the app, set `REACT_APP_DEBUG=true` in `.env` to be able to move past screens where the app is waiting on the robot. The default is `REACT_APP_DEBUG=false`.
3435
- If users will be accessing the app on a device other than the device running ROS, change `REACT_APP_ROS_SERVER_HOSTNAME` in `.env` to be the hostname of the device running ROS. Ensure that device is configured so that ports 8080 (web_video_server default) and 9090 (rosbridge default) can be accessed.
3536
3. Start the app: `npm start`
36-
4. Start the WebRTC signalling server: `node --env-file=.env server.js`
37+
4. Start the WebRTC signalling server: `pm2 start server.js` (see [here](https://pm2.keymetrics.io/docs/usage/quick-start/) for `pm2`` instructions)
3738
5. Start the headless robot browser: `node start_robot_browser.js`
3839
6. Use a web browser to navigate to `localhost:3000` to see the application.
3940

feedingwebapp/package-lock.json

Lines changed: 15 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

feedingwebapp/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"bootstrap": "^5.1.3",
1414
"caniuse-lite": "^1.0.30001579",
1515
"cors": "^2.8.5",
16+
"dotenv": "^16.4.1",
1617
"express": "^4.18.2",
1718
"mdb-react-ui-kit": "^3.0.0",
1819
"minimist": "^1.2.8",

feedingwebapp/server.js

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
const SegfaultHandler = require('segfault-handler')
1010
SegfaultHandler.registerHandler('crash.log')
11+
require('dotenv').config()
1112
const express = require('express')
1213
const app = express()
1314
const bodyParser = require('body-parser')
@@ -40,86 +41,86 @@ app.post('/subscribe', async ({ body }, res) => {
4041
]
4142
})
4243
if (debug) {
43-
console.log(Date(Date.now()).toString(), "subscribe: created peer connection object")
44+
console.log(Date(Date.now()).toString(), 'subscribe: created peer connection object')
4445
}
4546

4647
// Close any old peers on the same IP address
4748
const topic = body.topic
4849
if (debug) {
49-
console.log(Date(Date.now()).toString(), "subscribe: got topic", topic)
50+
console.log(Date(Date.now()).toString(), 'subscribe: got topic', topic)
5051
}
5152
const key = body.ip + ':' + topic
5253
if (debug) {
53-
console.log(Date(Date.now()).toString(), "subscribe: got key", key)
54+
console.log(Date(Date.now()).toString(), 'subscribe: got key', key)
5455
}
5556
if (key in subscribePeers && subscribePeers[key] && subscribePeers[key].connectionState !== 'closed') {
5657
if (debug) {
57-
console.log(Date(Date.now()).toString(), "subscribe: peer for key already exists")
58+
console.log(Date(Date.now()).toString(), 'subscribe: peer for key already exists')
5859
}
5960
const senders = subscribePeers[key].getSenders()
6061
if (debug) {
61-
console.log(Date(Date.now()).toString(), "subscribe: got senders for peer for key")
62+
console.log(Date(Date.now()).toString(), 'subscribe: got senders for peer for key')
6263
}
6364
senders.forEach((sender) => subscribePeers[key].removeTrack(sender))
6465
if (debug) {
65-
console.log(Date(Date.now()).toString(), "subscribe: removed tracks")
66+
console.log(Date(Date.now()).toString(), 'subscribe: removed tracks')
6667
}
6768
subscribePeers[key].close()
6869
if (debug) {
69-
console.log(Date(Date.now()).toString(), "subscribe: closed peer connection")
70+
console.log(Date(Date.now()).toString(), 'subscribe: closed peer connection')
7071
}
7172
}
7273
subscribePeers[key] = peer
7374
if (debug) {
74-
console.log(Date(Date.now()).toString(), "subscribe: set new peer connection")
75+
console.log(Date(Date.now()).toString(), 'subscribe: set new peer connection')
7576
}
7677

7778
const desc = new webrtc.RTCSessionDescription(body.sdp)
7879
if (debug) {
79-
console.log(Date(Date.now()).toString(), "subscribe: created desc")
80+
console.log(Date(Date.now()).toString(), 'subscribe: created desc')
8081
}
8182
await peer.setRemoteDescription(desc)
8283
if (debug) {
83-
console.log(Date(Date.now()).toString(), "subscribe: set remote desc")
84+
console.log(Date(Date.now()).toString(), 'subscribe: set remote desc')
8485
}
8586

8687
// Add the publisher's video stream to the subscriber's peer connection
8788
if (topic in senderStream) {
8889
if (debug) {
89-
console.log(Date(Date.now()).toString(), "subscribe: adding topics from publisher")
90+
console.log(Date(Date.now()).toString(), 'subscribe: adding topics from publisher')
9091
}
9192
senderStream[topic].getTracks().forEach((track) => peer.addTrack(track, senderStream[topic]))
9293
if (debug) {
93-
console.log(Date(Date.now()).toString(), "subscribe: added topics from publisher")
94+
console.log(Date(Date.now()).toString(), 'subscribe: added topics from publisher')
9495
}
9596
}
9697

9798
// Create an answer to the publisher's offer
9899
const answer = await peer.createAnswer()
99100
if (debug) {
100-
console.log(Date(Date.now()).toString(), "subscribe: created answer")
101+
console.log(Date(Date.now()).toString(), 'subscribe: created answer')
101102
}
102103
await peer.setLocalDescription(answer)
103104
if (debug) {
104-
console.log(Date(Date.now()).toString(), "subscribe: set local description")
105+
console.log(Date(Date.now()).toString(), 'subscribe: set local description')
105106
}
106107
const payload = {
107-
sdp: answer
108+
sdp: peer.localDescription
108109
}
109110
if (debug) {
110-
console.log(Date(Date.now()).toString(), "subscribe: created payload")
111+
console.log(Date(Date.now()).toString(), 'subscribe: created payload')
111112
}
112113

113114
// Send the answer to the publisher
114115
res.json(payload)
115116
if (debug) {
116-
console.log(Date(Date.now()).toString(), "subscribe: set payload")
117+
console.log(Date(Date.now()).toString(), 'subscribe: set payload')
117118
}
118119
} catch (err) {
119120
console.error(Date(Date.now()).toString(), 'subscribe: Failed to process subscriber, exception: ' + err.message)
120121
res.sendStatus(500)
121122
if (debug) {
122-
console.log(Date(Date.now()).toString(), "subscribe: sent error status 500")
123+
console.log(Date(Date.now()).toString(), 'subscribe: sent error status 500')
123124
}
124125
}
125126
})
@@ -137,80 +138,80 @@ app.post('/publish', async ({ body }, res) => {
137138
]
138139
})
139140
if (debug) {
140-
console.log(Date(Date.now()).toString(), "publish: create peer")
141+
console.log(Date(Date.now()).toString(), 'publish: create peer')
141142
}
142143

143144
// Close any old peers on the same IP address
144145
const topic = body.topic
145146
if (debug) {
146-
console.log(Date(Date.now()).toString(), "publish: got topic", topic)
147+
console.log(Date(Date.now()).toString(), 'publish: got topic', topic)
147148
}
148149
const key = body.ip + ':' + topic
149150
if (debug) {
150-
console.log(Date(Date.now()).toString(), "publish: got key", key)
151+
console.log(Date(Date.now()).toString(), 'publish: got key', key)
151152
}
152153
if (key in publishPeers && publishPeers[key] && publishPeers[key].connectionState !== 'closed') {
153154
if (debug) {
154-
console.log(Date(Date.now()).toString(), "publish: found existing publisher for key", key)
155+
console.log(Date(Date.now()).toString(), 'publish: found existing publisher for key', key)
155156
}
156157
const senders = publishPeers[key].getSenders()
157158
if (debug) {
158-
console.log(Date(Date.now()).toString(), "publish: got senders for old key")
159+
console.log(Date(Date.now()).toString(), 'publish: got senders for old key')
159160
}
160161
senders.forEach((sender) => publishPeers[key].removeTrack(sender))
161162
if (debug) {
162-
console.log(Date(Date.now()).toString(), "publish: removed tracks for old key")
163+
console.log(Date(Date.now()).toString(), 'publish: removed tracks for old key')
163164
}
164165
publishPeers[key].close()
165166
if (debug) {
166-
console.log(Date(Date.now()).toString(), "publish: closed old peer connection")
167+
console.log(Date(Date.now()).toString(), 'publish: closed old peer connection')
167168
}
168169
}
169170
publishPeers[key] = peer
170171
if (debug) {
171-
console.log(Date(Date.now()).toString(), "publish: added new peer")
172+
console.log(Date(Date.now()).toString(), 'publish: added new peer')
172173
}
173174

174175
// Send the publisher's video stream to all subscribers on that topic
175176
peer.ontrack = (e) => handleTrackEvent(e, topic)
176177
if (debug) {
177-
console.log(Date(Date.now()).toString(), "publish: handled track events")
178+
console.log(Date(Date.now()).toString(), 'publish: handled track events')
178179
}
179180

180181
// Create an answer to the publisher's offer
181182
const desc = new webrtc.RTCSessionDescription(body.sdp)
182183
if (debug) {
183-
console.log(Date(Date.now()).toString(), "publish: got desc")
184+
console.log(Date(Date.now()).toString(), 'publish: got desc')
184185
}
185186
await peer.setRemoteDescription(desc)
186187
if (debug) {
187-
console.log(Date(Date.now()).toString(), "publish: set remote description")
188+
console.log(Date(Date.now()).toString(), 'publish: set remote description')
188189
}
189190
const answer = await peer.createAnswer()
190191
if (debug) {
191-
console.log(Date(Date.now()).toString(), "publish: got answer")
192+
console.log(Date(Date.now()).toString(), 'publish: got answer')
192193
}
193194
await peer.setLocalDescription(answer)
194195
if (debug) {
195-
console.log(Date(Date.now()).toString(), "publish: set local description")
196+
console.log(Date(Date.now()).toString(), 'publish: set local description')
196197
}
197198
const payload = {
198-
sdp: answer
199+
sdp: peer.localDescription
199200
}
200201
if (debug) {
201-
console.log(Date(Date.now()).toString(), "publish: created payload")
202+
console.log(Date(Date.now()).toString(), 'publish: created payload')
202203
}
203204

204205
// Send the answer to the publisher
205206
res.json(payload)
206207
if (debug) {
207-
console.log(Date(Date.now()).toString(), "publish: set json payload")
208+
console.log(Date(Date.now()).toString(), 'publish: set json payload')
208209
}
209210
} catch (err) {
210211
console.error(Date(Date.now()).toString(), 'publish: Failed to process publisher, exception: ' + err.message)
211212
res.sendStatus(500)
212213
if (debug) {
213-
console.log(Date(Date.now()).toString(), "publish: sent error status 500")
214+
console.log(Date(Date.now()).toString(), 'publish: sent error status 500')
214215
}
215216
}
216217
})

feedingwebapp/src/Pages/GlobalState.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ export const useGlobalState = create(
128128
// Flag to indicate robot motion trough teleoperation interface
129129
teleopIsMoving: false,
130130
// Flag to indicate whether to auto-continue after face detection
131-
faceDetectionAutoContinue: false,
131+
faceDetectionAutoContinue: true,
132132
// Whether the settings bite transfer page is currently at the user's face
133133
// or not. This is in the off-chance that the mealState is not at the user's
134134
// face, the settings page is, and the user refreshes -- the page should

feedingwebapp/src/Pages/Home/MealStates/DetectingFace.jsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ const DetectingFace = (props) => {
3939
// Font size for text
4040
let textFontSize = 3
4141
// let buttonWidth = 22
42-
let buttonHeight = 14
42+
let buttonHeight = 12
4343
let iconWidth = 20
44-
let iconHeight = 12
44+
let iconHeight = 10
4545
let sizeSuffix = isPortrait ? 'vh' : 'vw'
4646

4747
/**

feedingwebapp/src/buttons/MaskButton.jsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,20 @@ function MaskButton(props) {
4646
const drawCircle = useCallback(() => {
4747
// Get the canvas
4848
const canvas = canvasRef.current
49-
// Get the context
50-
const ctx = canvas.getContext('2d')
51-
// Clear the canvas
52-
ctx.clearRect(0, 0, canvas.width, canvas.height)
53-
// Draw a red filled circle
54-
let radius = 5
55-
ctx.beginPath()
56-
ctx.arc(canvas.width / 2, canvas.height / 2, radius, 0, 2 * Math.PI)
57-
ctx.fillStyle = 'red'
58-
ctx.fill()
49+
// Canvas might be null if the previous render had more MaskButtons
50+
// than the current render.
51+
if (canvas !== null) {
52+
// Get the context
53+
const ctx = canvas.getContext('2d')
54+
// Clear the canvas
55+
ctx.clearRect(0, 0, canvas.width, canvas.height)
56+
// Draw a red filled circle
57+
let radius = 5
58+
ctx.beginPath()
59+
ctx.arc(canvas.width / 2, canvas.height / 2, radius, 0, 2 * Math.PI)
60+
ctx.fillStyle = 'red'
61+
ctx.fill()
62+
}
5963
}, [])
6064

6165
// Draw a red filled circle on the middle of the canvas

feedingwebapp/src/webrtc/webrtc_helpers.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ export class WebRTCConnection {
4343

4444
this.peerConnection.onnegotiationneeded = async () => {
4545
try {
46+
console.log('onnegotiationneeded')
4647
const offer = await this.peerConnection.createOffer()
4748
await this.peerConnection.setLocalDescription(offer)
4849
const ip = await this.getIPAddress()
@@ -61,6 +62,7 @@ export class WebRTCConnection {
6162
}
6263

6364
this.peerConnection.oniceconnectionstatechange = () => {
65+
console.log('oniceconnectionstatechange')
6466
if (!this.peerConnection) throw new Error('peerConnection is undefined')
6567
if (this.peerConnection.iceConnectionState === 'failed') {
6668
this.peerConnection.restartIce()

feedingwebapp/start_robot_browser.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ if (argv.port) {
4141
]
4242
})
4343
const page = await browser.newPage()
44+
page.on('console', (msg) => {
45+
console.log(msg)
46+
})
4447

4548
while (num_tries < max_tries) {
4649
try {

0 commit comments

Comments
 (0)