From c9f729e4bae55d2e59733d934d031b7e63941ce2 Mon Sep 17 00:00:00 2001 From: Geeoon Chung Date: Wed, 17 Jul 2024 01:41:58 -0700 Subject: [PATCH 01/12] Update README.mD --- README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/README.md b/README.md index 7f42f60..671b168 100644 --- a/README.md +++ b/README.md @@ -380,6 +380,38 @@ Sent from the rover server to inform Mission Control of a single frame of a came - `camera` - the name of the camera: `mast|hand|wrist` - `data` - the raw h264 frame data, or `null` if no data is available +## Camera Frame Request +### Description +Sent from Mission Control to instruct the rover server to send a Camera Frame Report. + +### Syntax +``` +{ + type: "cameraFrameRequest", + camera: string +} +``` + +### Parameters +- `camera` - the name of the camera + +## Camera Frame Report +### Description +Sent from the rover server to inform Mission Control of a full resolution lossless camera frame. + +### Syntax +``` +{ + type: "cameraFrameReport", + camera: string, + data: string | null +} +``` + +### Parameters +- `camera` - the name of the camera +- `data` - the image, base64 encoded + ## Autonomous Waypoint Navigation Request ### Description From 337dcdc748d03d1fd5a4ce146a4ac88c4989bba2 Mon Sep 17 00:00:00 2001 From: Geeoon Chung Date: Wed, 17 Jul 2024 01:42:53 -0700 Subject: [PATCH 02/12] Bump MCP version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 671b168..e4658db 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ The rover can be operated through Mission Control with either a keyboard or two ![Armo controls](/src/components/help/armControls.png) ![Keyboard controls](/src/components/help/keyboardControls.png) -## Messages (`v2024.1.2`) +## Messages (`v2024.2.0`) The JSON objects sent between Mission Control and the rover server are termed *messages*. Each message has a type property and a number of additional parameters depending on the type. The usage of each type of message is detailed below. ## Mounted Peripheral Report From 2e827590788807e2042ca2aeaa099433a15e9816 Mon Sep 17 00:00:00 2001 From: Geeoon Chung Date: Wed, 17 Jul 2024 02:18:35 -0700 Subject: [PATCH 03/12] Add cameraFrameReport if --- src/store/middleware/camerasMiddleware.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/store/middleware/camerasMiddleware.js b/src/store/middleware/camerasMiddleware.js index 4e9418d..9c38a15 100644 --- a/src/store/middleware/camerasMiddleware.js +++ b/src/store/middleware/camerasMiddleware.js @@ -74,11 +74,14 @@ const camerasMiddleware = store => next => action => { case messageReceivedFromRover.type: { const { message } = action.payload; - if (message.type === "cameraStreamReport") + if (message.type === "cameraStreamReport") { store.dispatch(cameraStreamDataReportReceived({ cameraName: message.camera, frameData: message.data })); + } else if (message.type === "cameraFrameReport") { + alert("Received frame."); + } break; } From 23abf31d24ca9b50e403b65ed02ca63fd191ac68 Mon Sep 17 00:00:00 2001 From: Geeoon Chung Date: Wed, 17 Jul 2024 02:25:39 -0700 Subject: [PATCH 04/12] Middleware and slice for request --- src/store/camerasSlice.js | 5 ++++- src/store/middleware/camerasMiddleware.js | 11 +++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/store/camerasSlice.js b/src/store/camerasSlice.js index da3932b..295328c 100644 --- a/src/store/camerasSlice.js +++ b/src/store/camerasSlice.js @@ -25,6 +25,8 @@ const camerasSlice = createSlice({ state[cameraName].frameData = null; }, + requestCameraFrame() {}, + cameraStreamDataReportReceived(state, action) { const { cameraName, frameData } = action.payload; if (state[cameraName].isStreaming) @@ -36,7 +38,8 @@ const camerasSlice = createSlice({ export const { openCameraStream, closeCameraStream, - cameraStreamDataReportReceived + cameraStreamDataReportReceived, + requestCameraFrame } = camerasSlice.actions; export const selectAllCameraNames = state => Object.keys(state.cameras); diff --git a/src/store/middleware/camerasMiddleware.js b/src/store/middleware/camerasMiddleware.js index 9c38a15..80fc0e4 100644 --- a/src/store/middleware/camerasMiddleware.js +++ b/src/store/middleware/camerasMiddleware.js @@ -2,6 +2,7 @@ import { openCameraStream, closeCameraStream, cameraStreamDataReportReceived, + requestCameraFrame, } from "../camerasSlice"; import { messageReceivedFromRover, @@ -39,6 +40,16 @@ const camerasMiddleware = store => next => action => { break; } + case requestCameraFrame.type: { + store.dispatch(messageRover({ + message: { + type: "cameraFrameRequest", + camera: action.payload.cameraName + } + })); + break; + } + case roverConnected.type: { // Inform the rover of camera streams we would like to receive when we // connect. From a83567bd2b8f9964d07cbafba8c69faa0d897b1a Mon Sep 17 00:00:00 2001 From: Geeoon Chung Date: Wed, 17 Jul 2024 02:35:51 -0700 Subject: [PATCH 05/12] requestDownloadFrame in CameraStream --- src/components/camera/CameraStream.jsx | 51 ++++++++++++++------------ 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/src/components/camera/CameraStream.jsx b/src/components/camera/CameraStream.jsx index 6293773..711df14 100644 --- a/src/components/camera/CameraStream.jsx +++ b/src/components/camera/CameraStream.jsx @@ -4,7 +4,8 @@ import JMuxer from "jmuxer"; import { openCameraStream, closeCameraStream, - selectCameraStreamFrameData + selectCameraStreamFrameData, + requestCameraFrame, } from "../../store/camerasSlice"; import camelCaseToTitle from "../../util/camelCaseToTitle"; import "./CameraStream.css"; @@ -65,26 +66,26 @@ async function createPopOutWindow(cameraTitle, cameraName, unloadCallback, video * /public/camera/cam_popout.js. If you make changes to this function, you need to * make corresponding changes to the cam_popout.js file. */ -function downloadCurrentFrame(video, cameraTitle) { - if (!video || !(video.videoWidth && video.videoHeight)) return null; - let canvas = document.createElement('canvas'); - let context = canvas.getContext('2d'); - canvas.width = video.videoWidth; - canvas.height = video.videoHeight; - context.drawImage(video, 0, 0, canvas.width, canvas.height); - - let link = document.createElement("a"); - link.href = canvas.toDataURL("image/jpeg", 1); - - let time = new Date(); - let timezoneOffset = time.getTimezoneOffset() * 60000; - let timeString = new Date(time - timezoneOffset).toISOString().replace(":", "_").substring(0, 19); - - link.download = `${cameraTitle}-${timeString}.jpg`; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); -} +// function downloadCurrentFrame() { + // if (!video || !(video.videoWidth && video.videoHeight)) return null; + // let canvas = document.createElement('canvas'); + // let context = canvas.getContext('2d'); + // canvas.width = video.videoWidth; + // canvas.height = video.videoHeight; + // context.drawImage(video, 0, 0, canvas.width, canvas.height); + + // let link = document.createElement("a"); + // link.href = canvas.toDataURL("image/jpeg", 1); + + // let time = new Date(); + // let timezoneOffset = time.getTimezoneOffset() * 60000; + // let timeString = new Date(time - timezoneOffset).toISOString().replace(":", "_").substring(0, 19); + + // link.download = `${cameraTitle}-${timeString}.jpg`; + // document.body.appendChild(link); + // link.click(); + // document.body.removeChild(link); +// } function CameraStream({ cameraName }) { const dispatch = useDispatch(); @@ -112,7 +113,11 @@ function CameraStream({ cameraName }) { const vidTag = useMemo(() => { return ; - }, [cameraName, cameraTitle, popoutWindow]) + }, [cameraName, cameraTitle, popoutWindow]); + + function requestDownloadFrame() { + dispatch(requestCameraFrame(cameraName)); + }; const drawFrameOnExt = useCallback((window, last_ww, last_wh) => { if (vidTag && window && cameraCanvas) { @@ -238,7 +243,7 @@ function CameraStream({ cameraName }) {
From dda7fae9762e03c6b700bbca7939e91d2e5ecfdc Mon Sep 17 00:00:00 2001 From: Geeoon Chung Date: Wed, 17 Jul 2024 02:37:10 -0700 Subject: [PATCH 06/12] Rename alert in camerasMiddleware --- src/store/middleware/camerasMiddleware.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/middleware/camerasMiddleware.js b/src/store/middleware/camerasMiddleware.js index 80fc0e4..32acacb 100644 --- a/src/store/middleware/camerasMiddleware.js +++ b/src/store/middleware/camerasMiddleware.js @@ -91,7 +91,7 @@ const camerasMiddleware = store => next => action => { frameData: message.data })); } else if (message.type === "cameraFrameReport") { - alert("Received frame."); + alert("camerasMiddleware received frame."); } break; } From 7693ed7018123ddd151177b85f098ddca61fdb76 Mon Sep 17 00:00:00 2001 From: Geeoon Chung Date: Wed, 17 Jul 2024 20:10:28 -0700 Subject: [PATCH 07/12] Fixed requestCameraFrame dispatch usage --- src/components/camera/CameraStream.jsx | 6 +++--- src/store/middleware/camerasMiddleware.js | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/components/camera/CameraStream.jsx b/src/components/camera/CameraStream.jsx index 711df14..433db65 100644 --- a/src/components/camera/CameraStream.jsx +++ b/src/components/camera/CameraStream.jsx @@ -115,9 +115,9 @@ function CameraStream({ cameraName }) { return ; }, [cameraName, cameraTitle, popoutWindow]); - function requestDownloadFrame() { - dispatch(requestCameraFrame(cameraName)); - }; + const requestDownloadFrame = useCallback(() => { + dispatch(requestCameraFrame({ cameraName })); + }, [cameraName, dispatch]); const drawFrameOnExt = useCallback((window, last_ww, last_wh) => { if (vidTag && window && cameraCanvas) { diff --git a/src/store/middleware/camerasMiddleware.js b/src/store/middleware/camerasMiddleware.js index 32acacb..4066af7 100644 --- a/src/store/middleware/camerasMiddleware.js +++ b/src/store/middleware/camerasMiddleware.js @@ -42,10 +42,8 @@ const camerasMiddleware = store => next => action => { case requestCameraFrame.type: { store.dispatch(messageRover({ - message: { - type: "cameraFrameRequest", - camera: action.payload.cameraName - } + type: "cameraFrameRequest", + camera: action.payload.cameraName })); break; } From 472873f2dd59e08313fb47691e6cb8a28e05941a Mon Sep 17 00:00:00 2001 From: Geeoon Chung Date: Wed, 17 Jul 2024 22:49:42 -0700 Subject: [PATCH 08/12] Popout window download --- public/camera/cam_popout.js | 28 ---------------- src/components/camera/CameraStream.jsx | 41 +++-------------------- src/store/middleware/camerasMiddleware.js | 22 ++++++++++-- 3 files changed, 24 insertions(+), 67 deletions(-) delete mode 100644 public/camera/cam_popout.js diff --git a/public/camera/cam_popout.js b/public/camera/cam_popout.js deleted file mode 100644 index fe2183b..0000000 --- a/public/camera/cam_popout.js +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Note: This is the vanilla JS version of this function for the popout window. - * The CameraStream component "download" button uses a seperate function, defined in - * the CameraStream.jsx file. If you make changes to this function, you need to - * make corresponding changes to the CameraStream.jsx file. - */ -function download(title, width, height) { - let canvas = document.getElementById("ext-vid"); - - let time = new Date(); - let timezoneOffset = time.getTimezoneOffset() * 60000; - let timeString = new Date(time - timezoneOffset).toISOString().replace(":", "_").substring(0, 19); - - let link = document.createElement("a"); - - let tempCanvas = document.createElement('canvas'); - tempCanvas.width = width; - tempCanvas.height = height; - - let tempContext = tempCanvas.getContext('2d'); - tempContext.drawImage(canvas, 0, 0, tempCanvas.width, tempCanvas.height); - - link.href = tempCanvas.toDataURL("image/jpeg", 1); - link.download = `${title}-${timeString}.jpg`; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); -} \ No newline at end of file diff --git a/src/components/camera/CameraStream.jsx b/src/components/camera/CameraStream.jsx index 433db65..2543854 100644 --- a/src/components/camera/CameraStream.jsx +++ b/src/components/camera/CameraStream.jsx @@ -15,19 +15,18 @@ import "./CameraStream.css"; * cameraTitle: the camera title, * cameraName: the camera name, * unloadCallback: a callback that is ran before the window is fully unloaded - * video_width: the stream width - * video_height: the stream height + * downloadCallback: a callback that is ran when the download button is pressed * Returns: Promise of an object with keys: window, canvas, context, aspectRatio */ -async function createPopOutWindow(cameraTitle, cameraName, unloadCallback, video_width, video_height) { +async function createPopOutWindow(cameraTitle, cameraName, unloadCallback, downloadCallback) { let newWindow = window.open("/camera/cam_popout.htm", "", "width=500,height=500"); const returnPromise = new Promise((resolve, reject) => { newWindow.onload = () => { newWindow.document.title = `${cameraTitle} Stream`; newWindow.document.querySelector('#ext-title').innerText = cameraTitle; - newWindow.document.querySelector('#ext-download-button').setAttribute("onclick", `download("${cameraTitle}", ${video_width}, ${video_height})`); - + // newWindow.document.querySelector('#ext-download-button').setAttribute("onclick", `download("${cameraTitle}", ${video_width}, ${video_height})`); + newWindow.document.querySelector('#ext-download-button').onclick = downloadCallback; let canvas = newWindow.document.querySelector('#ext-vid'); let context = canvas.getContext('2d'); let aspectRatio = document.querySelector(`#${cameraName}-player`).videoHeight / document.querySelector(`#${cameraName}-player`).videoWidth; @@ -57,36 +56,6 @@ async function createPopOutWindow(cameraTitle, cameraName, unloadCallback, video return returnPromise; } -/** - * Takes - * video: HTMLVideoElement representing the video tag which should be processed. - * cameraTitle: name of the camera, used for the filename. - * Note: This is the React version of this function for this CameraStream component, - * The popout window "download" button uses a seperate function, defined in - * /public/camera/cam_popout.js. If you make changes to this function, you need to - * make corresponding changes to the cam_popout.js file. - */ -// function downloadCurrentFrame() { - // if (!video || !(video.videoWidth && video.videoHeight)) return null; - // let canvas = document.createElement('canvas'); - // let context = canvas.getContext('2d'); - // canvas.width = video.videoWidth; - // canvas.height = video.videoHeight; - // context.drawImage(video, 0, 0, canvas.width, canvas.height); - - // let link = document.createElement("a"); - // link.href = canvas.toDataURL("image/jpeg", 1); - - // let time = new Date(); - // let timezoneOffset = time.getTimezoneOffset() * 60000; - // let timeString = new Date(time - timezoneOffset).toISOString().replace(":", "_").substring(0, 19); - - // link.download = `${cameraTitle}-${timeString}.jpg`; - // document.body.appendChild(link); - // link.click(); - // document.body.removeChild(link); -// } - function CameraStream({ cameraName }) { const dispatch = useDispatch(); useEffect(() => { @@ -157,7 +126,7 @@ function CameraStream({ cameraName }) { } else if (vidTag) { // if the window popout doesn't exist let video = document.getElementById(vidTag.props.id); - let { popout, canvas, context, aspectRatio } = await createPopOutWindow(cameraTitle, cameraName, () => setPopoutWindow(null), video.videoWidth, video.videoHeight); + let { popout, canvas, context, aspectRatio } = await createPopOutWindow(cameraTitle, cameraName, () => setPopoutWindow(null), requestDownloadFrame); setAspectRatio(aspectRatio); setPopoutWindow(popout); cameraCanvas.current = canvas; diff --git a/src/store/middleware/camerasMiddleware.js b/src/store/middleware/camerasMiddleware.js index 4066af7..1b9c4fb 100644 --- a/src/store/middleware/camerasMiddleware.js +++ b/src/store/middleware/camerasMiddleware.js @@ -10,6 +10,8 @@ import { roverConnected, roverDisconnected } from "../roverSocketSlice"; +import camelCaseToTitle from "../../util/camelCaseToTitle"; + /** * Middleware that handles requesting and receiving camera streams from the @@ -42,8 +44,10 @@ const camerasMiddleware = store => next => action => { case requestCameraFrame.type: { store.dispatch(messageRover({ - type: "cameraFrameRequest", - camera: action.payload.cameraName + message: { + type: "cameraFrameRequest", + camera: action.payload.cameraName + } })); break; } @@ -89,7 +93,19 @@ const camerasMiddleware = store => next => action => { frameData: message.data })); } else if (message.type === "cameraFrameReport") { - alert("camerasMiddleware received frame."); + if (message.data !== "") { + let link = document.createElement("a"); + link.href = `data:image/jpg;base64,${message.data}`; + let time = new Date(); + let timezoneOffset = time.getTimezoneOffset() * 60000; + let timeString = new Date(time - timezoneOffset).toISOString().replace(":", "_").substring(0, 19); + + link.download = `${camelCaseToTitle(message.camera)}-${timeString}.jpg`; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + + } } break; } From 45e22dd144c14bfa5b856b9a977456b70ca338a4 Mon Sep 17 00:00:00 2001 From: Geeoon Chung Date: Wed, 17 Jul 2024 23:07:55 -0700 Subject: [PATCH 09/12] Disable button if rover disconnects --- src/components/camera/CameraStream.jsx | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/components/camera/CameraStream.jsx b/src/components/camera/CameraStream.jsx index 2543854..5359265 100644 --- a/src/components/camera/CameraStream.jsx +++ b/src/components/camera/CameraStream.jsx @@ -7,9 +7,9 @@ import { selectCameraStreamFrameData, requestCameraFrame, } from "../../store/camerasSlice"; +import { selectRoverIsConnected } from "../../store/roverSocketSlice"; import camelCaseToTitle from "../../util/camelCaseToTitle"; import "./CameraStream.css"; - /** * Takes: * cameraTitle: the camera title, @@ -67,6 +67,7 @@ function CameraStream({ cameraName }) { }; }, [cameraName, dispatch]); + const roverIsConnected = useSelector(selectRoverIsConnected); const frameDataArray = useSelector(selectCameraStreamFrameData(cameraName)); const cameraTitle = camelCaseToTitle(cameraName); const [hasRendered, setHasRendered] = useState(false); @@ -77,6 +78,7 @@ function CameraStream({ cameraName }) { const [popoutWindow, setPopoutWindow] = useState(null); const [aspectRatio, setAspectRatio] = useState(1); + const cameraCanvas = useRef(null); // used for popout window const cameraContext = useRef(null); // used for popout window @@ -105,18 +107,20 @@ function CameraStream({ cameraName }) { } } - if (hasFrame) { - let button = window.document.querySelector("#ext-download-button"); - button.removeAttribute('disabled'); - } - let video = document.querySelector(`#${vidTag.props.id}`); cameraContext.current.drawImage(video, 0, 0, cameraCanvas.current.width, cameraCanvas.current.height); last_ww = window.innerWidth; last_wh = window.innerHeight; window.requestAnimationFrame(() => { drawFrameOnExt(window, last_ww, last_wh); }); } - }, [vidTag, aspectRatio, hasFrame]); + }, [vidTag, aspectRatio, hasFrame, roverIsConnected]); + + useEffect(() => { + if (popoutWindow) { + let button = popoutWindow.document.querySelector("#ext-download-button"); + if (button) button.disabled = !(hasFrame && roverIsConnected); + } + }, [popoutWindow, hasFrame, roverIsConnected]); const handlePopOut = useCallback(async () => { if (popoutWindow) { @@ -212,7 +216,7 @@ function CameraStream({ cameraName }) {
From 470151e6ee849bd8ddf679e4a6886c4992292caf Mon Sep 17 00:00:00 2001 From: Geeoon Chung Date: Wed, 17 Jul 2024 23:29:52 -0700 Subject: [PATCH 10/12] Formatting --- src/components/camera/CameraStream.jsx | 35 +++++++++++------------ src/store/camerasSlice.js | 2 +- src/store/middleware/camerasMiddleware.js | 2 +- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/components/camera/CameraStream.jsx b/src/components/camera/CameraStream.jsx index 5359265..0e6f71c 100644 --- a/src/components/camera/CameraStream.jsx +++ b/src/components/camera/CameraStream.jsx @@ -25,7 +25,6 @@ async function createPopOutWindow(cameraTitle, cameraName, unloadCallback, downl newWindow.onload = () => { newWindow.document.title = `${cameraTitle} Stream`; newWindow.document.querySelector('#ext-title').innerText = cameraTitle; - // newWindow.document.querySelector('#ext-download-button').setAttribute("onclick", `download("${cameraTitle}", ${video_width}, ${video_height})`); newWindow.document.querySelector('#ext-download-button').onclick = downloadCallback; let canvas = newWindow.document.querySelector('#ext-vid'); let context = canvas.getContext('2d'); @@ -37,7 +36,7 @@ async function createPopOutWindow(cameraTitle, cameraName, unloadCallback, downl window.onunload = () => { if (newWindow && !newWindow.closed) { - newWindow.close(); + newWindow.close(); } }; @@ -49,7 +48,7 @@ async function createPopOutWindow(cameraTitle, cameraName, unloadCallback, downl context: context, aspectRatio: aspectRatio }; - resolve(output); + resolve(output); } }); @@ -81,19 +80,19 @@ function CameraStream({ cameraName }) { const cameraCanvas = useRef(null); // used for popout window const cameraContext = useRef(null); // used for popout window - + const vidTag = useMemo(() => { - return ; + return ; }, [cameraName, cameraTitle, popoutWindow]); const requestDownloadFrame = useCallback(() => { dispatch(requestCameraFrame({ cameraName })); }, [cameraName, dispatch]); - + const drawFrameOnExt = useCallback((window, last_ww, last_wh) => { if (vidTag && window && cameraCanvas) { // draw it onto the popout window - + if (window.innerWidth !== last_ww || window.innerHeight !== last_wh) { // if the window is wider than the stream if (window.innerHeight / window.innerWidth > aspectRatio) { @@ -113,7 +112,7 @@ function CameraStream({ cameraName }) { last_wh = window.innerHeight; window.requestAnimationFrame(() => { drawFrameOnExt(window, last_ww, last_wh); }); } - }, [vidTag, aspectRatio, hasFrame, roverIsConnected]); + }, [vidTag, aspectRatio]); useEffect(() => { if (popoutWindow) { @@ -147,10 +146,10 @@ function CameraStream({ cameraName }) { flushingTime: 0, maxDelay: 50, clearBuffer: true, - onError: function(data) { + onError: function (data) { console.warn('Buffer error encountered', data); }, - + onMissingVideoFrames: function (data) { console.warn('Video frames missing', data); } @@ -194,7 +193,7 @@ function CameraStream({ cameraName }) { } }; }, [popoutWindow]); - + useEffect(() => { // this indicates that the site has rendered and the player is able to be modified (specifically the src) setHasRendered(true); @@ -203,20 +202,20 @@ function CameraStream({ cameraName }) { return (

{cameraTitle}

-
{ vidTag }
- { popoutWindow ?

Stream In External Window

: (!frameDataArray &&

No Stream Available

) } +
{vidTag}
+ {popoutWindow ?

Stream In External Window

: (!frameDataArray &&

No Stream Available

)}
FPS: {currentFpsAvg && frameDataArray ? Math.round(currentFpsAvg) : 'N/A'}
- { popoutWindow ? 'Merge Window' : 'Pop Out' } + title={`Open "${cameraTitle}" camera stream in a new window.`} + onClick={handlePopOut}> + {popoutWindow ? 'Merge Window' : 'Pop Out'}
diff --git a/src/store/camerasSlice.js b/src/store/camerasSlice.js index 295328c..6cb4887 100644 --- a/src/store/camerasSlice.js +++ b/src/store/camerasSlice.js @@ -25,7 +25,7 @@ const camerasSlice = createSlice({ state[cameraName].frameData = null; }, - requestCameraFrame() {}, + requestCameraFrame() { }, cameraStreamDataReportReceived(state, action) { const { cameraName, frameData } = action.payload; diff --git a/src/store/middleware/camerasMiddleware.js b/src/store/middleware/camerasMiddleware.js index 1b9c4fb..5d4af12 100644 --- a/src/store/middleware/camerasMiddleware.js +++ b/src/store/middleware/camerasMiddleware.js @@ -99,7 +99,7 @@ const camerasMiddleware = store => next => action => { let time = new Date(); let timezoneOffset = time.getTimezoneOffset() * 60000; let timeString = new Date(time - timezoneOffset).toISOString().replace(":", "_").substring(0, 19); - + link.download = `${camelCaseToTitle(message.camera)}-${timeString}.jpg`; document.body.appendChild(link); link.click(); From 145334d33c145ab5160251e52e5a8ac38fd5040b Mon Sep 17 00:00:00 2001 From: Geeoon Chung Date: Thu, 18 Jul 2024 13:27:10 -0700 Subject: [PATCH 11/12] Addressed PR comments --- README.md | 2 +- src/store/middleware/camerasMiddleware.js | 23 ++++++++++------------- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index e4658db..245bb38 100644 --- a/README.md +++ b/README.md @@ -404,7 +404,7 @@ Sent from the rover server to inform Mission Control of a full resolution lossle { type: "cameraFrameReport", camera: string, - data: string | null + data: string } ``` diff --git a/src/store/middleware/camerasMiddleware.js b/src/store/middleware/camerasMiddleware.js index 5d4af12..4614706 100644 --- a/src/store/middleware/camerasMiddleware.js +++ b/src/store/middleware/camerasMiddleware.js @@ -92,20 +92,17 @@ const camerasMiddleware = store => next => action => { cameraName: message.camera, frameData: message.data })); - } else if (message.type === "cameraFrameReport") { - if (message.data !== "") { - let link = document.createElement("a"); - link.href = `data:image/jpg;base64,${message.data}`; - let time = new Date(); - let timezoneOffset = time.getTimezoneOffset() * 60000; - let timeString = new Date(time - timezoneOffset).toISOString().replace(":", "_").substring(0, 19); + } else if (message.type === "cameraFrameReport" && message.data !== "") { + let link = document.createElement("a"); + link.href = `data:image/jpg;base64,${message.data}`; + let time = new Date(); + let timezoneOffset = time.getTimezoneOffset() * 60000; + let timeString = new Date(time - timezoneOffset).toISOString().replace(":", "_").substring(0, 19); - link.download = `${camelCaseToTitle(message.camera)}-${timeString}.jpg`; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); - - } + link.download = `${camelCaseToTitle(message.camera)}-${timeString}.jpg`; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); } break; } From 7afc428bb7da6612ce62ee90a24d445fd5ddc00c Mon Sep 17 00:00:00 2001 From: Geeoon Chung Date: Sat, 20 Jul 2024 19:50:52 -0700 Subject: [PATCH 12/12] Update README.md --- README.md | 4 ++-- src/store/camerasSlice.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 245bb38..a267db3 100644 --- a/README.md +++ b/README.md @@ -382,7 +382,7 @@ Sent from the rover server to inform Mission Control of a single frame of a came ## Camera Frame Request ### Description -Sent from Mission Control to instruct the rover server to send a Camera Frame Report. +Sent from Mission Control to instruct the rover server to send a Camera Frame Report. If `camera` specifies a valid camera stream, the rover will respond with a Camera Frame Report containing the latest frame from that camera. ### Syntax ``` @@ -393,7 +393,7 @@ Sent from Mission Control to instruct the rover server to send a Camera Frame Re ``` ### Parameters -- `camera` - the name of the camera +- `camera` - the name of the camera: `mast|hand|wrist` ## Camera Frame Report ### Description diff --git a/src/store/camerasSlice.js b/src/store/camerasSlice.js index 6cb4887..1c93266 100644 --- a/src/store/camerasSlice.js +++ b/src/store/camerasSlice.js @@ -1,6 +1,6 @@ import { createSlice } from "@reduxjs/toolkit" -const cameraNames = ["mast", "hand", "wrist"]; +const cameraNames = ["mast", "hand", "wrist", "upperArm"]; const initialState = cameraNames.reduce((state, cameraName) => ({ ...state,