Skip to content

Commit

Permalink
Add services notification and unify colors
Browse files Browse the repository at this point in the history
  • Loading branch information
rafal-gorecki committed Jan 8, 2025
1 parent 17e3f47 commit 87d59c2
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 171 deletions.
3 changes: 2 additions & 1 deletion packages/studio-base/src/panels/Battery/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ body {

.battery__text {
margin-bottom: 0.3rem;
font-size: var(--normal-font-size);
}

.battery__percentage {
Expand All @@ -91,7 +92,7 @@ body {
}

.battery__status i {
font-size: 1.25rem;
font-size: var(--normal-font-size);
}

.battery__pill {
Expand Down
144 changes: 71 additions & 73 deletions packages/studio-base/src/panels/EStop/EStop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import * as _ from "lodash-es";
import { Dispatch, SetStateAction, useCallback, useEffect, useLayoutEffect, useMemo, useReducer, useState } from "react";
import { makeStyles } from "tss-react/mui";

import Log from "@foxglove/log";
import { parseMessagePath, MessagePath } from "@foxglove/message-path";
import { MessageEvent, PanelExtensionContext, SettingsTreeAction } from "@foxglove/studio";
import { simpleGetMessagePathDataItems } from "@foxglove/studio-base/components/MessagePathSyntax/simpleGetMessagePathDataItems";
import NotificationModal from "@foxglove/studio-base/components/NotificationModal";
import Stack from "@foxglove/studio-base/components/Stack";
import { Config } from "@foxglove/studio-base/panels/EStop/types";
import ThemeProvider from "@foxglove/studio-base/theme/ThemeProvider";
Expand All @@ -19,18 +19,16 @@ import { defaultConfig, settingsActionReducer, useSettingsTree } from "./setting

import "./styles.css";


const log = Log.getLogger(__dirname);

type Props = {
context: PanelExtensionContext;
};

type EStopState = "go" | "stop" | undefined;
type SrvResponse = { success: boolean; message: string };

type ReqState = {
type SrvState = {
status: "requesting" | "error" | "success";
value: string;
response: SrvResponse | undefined;
};

type State = {
Expand Down Expand Up @@ -161,7 +159,7 @@ function EStopContent(
// panel extensions must notify when they've completed rendering
// onRender will setRenderDone to a done callback which we can invoke after we've rendered
const [renderDone, setRenderDone] = useState<() => void>(() => () => { });
const [reqState, setReqState] = useState<ReqState | undefined>();
const [srvState, setSrvState] = useState<SrvState | undefined>();
const [eStopAction, setEStopAction] = useState<EStopState>();
const [config, setConfig] = useState<Config>(() => ({
...defaultConfig,
Expand All @@ -186,33 +184,28 @@ function EStopContent(
dispatch({ type: "path", path: config.statusTopicName });
}, [config.statusTopicName]);

useEffect(() => {
context.saveState(config);
context.setDefaultPanelTitle(
config.goServiceName ? `Unspecified` : undefined,
);
}, [config, context]);
const handleRequestCloseNotification = () => {
setSrvState(undefined);
};

useEffect(() => {
context.saveState(config);
context.setDefaultPanelTitle(
config.stopServiceName ? `Unspecified` : undefined,
);
context.setDefaultPanelTitle(`E-Stop`);
}, [config, context]);

useEffect(() => {
context.watch("colorScheme");

context.onRender = (renderReqState, done) => {
context.onRender = (renderSrvState, done) => {
setRenderDone(() => done);
setColorScheme(renderReqState.colorScheme ?? "light");
setColorScheme(renderSrvState.colorScheme ?? "light");

if (renderReqState.didSeek === true) {
if (renderSrvState.didSeek === true) {
dispatch({ type: "seek" });
}

if (renderReqState.currentFrame) {
dispatch({ type: "frame", messages: renderReqState.currentFrame });
if (renderSrvState.currentFrame) {
dispatch({ type: "frame", messages: renderSrvState.currentFrame });
}
};

Expand Down Expand Up @@ -263,37 +256,29 @@ function EStopContent(
config.goServiceName &&
config.stopServiceName &&
eStopAction != undefined &&
reqState?.status !== "requesting",
srvState?.status !== "requesting",
);

const eStopClicked = useCallback(async () => {
if (!context.callService) {
setReqState({ status: "error", value: "The data source does not allow calling services" });
setSrvState({ status: "error", response: undefined });
return;
}

const serviceName = eStopAction === "go" ? config.goServiceName : config.stopServiceName;

if (!serviceName) {
setReqState({ status: "error", value: "Service name is not configured" });
setSrvState({ status: "error", response: undefined });
return;
}

try {
setReqState({ status: "requesting", value: `Calling ${serviceName}...` });
setEStopAction(undefined);
const response = await context.callService(serviceName, {});
setReqState({
status: "success",
value: JSON.stringify(response, (_key, value) => (typeof value === "bigint" ? value.toString() : value), 2) ?? "",
});
} catch (err) {
setReqState({ status: "error", value: (err as Error).message });
log.error(err);
}
setSrvState({ status: "requesting", response: undefined });
const response = await context.callService(serviceName, {}) as SrvResponse;
setSrvState({ status: "success", response });

}, [context, eStopAction, config.goServiceName, config.stopServiceName]);

// Setting eStopAction based on state.latestMatchingQueriedData
// Setting eStopAction based on received state
useEffect(() => {
if (state.latestMatchingQueriedData != undefined) {
const data = state.latestMatchingQueriedData as boolean;
Expand All @@ -307,42 +292,55 @@ function EStopContent(
}, [renderDone]);

return (
<Stack flex="auto" gap={1} padding={1.5} position="relative" fullHeight>
<Stack justifyContent="center" alignItems="center" fullWidth fullHeight>
<div className="center">
<Stack
direction="column-reverse"
justifyContent="center"
alignItems="center"
overflow="hidden"
flexGrow={0}
gap={1.5}
>
{statusMessage && (
<Typography variant="caption" noWrap>
{statusMessage}
</Typography>
)}
<span>
<Button
className={classes.button}
variant="contained"
disabled={!canEStop}
onClick={eStopClicked}
data-testid="call-service-button"
style={{
minWidth: "150px",
minHeight: "150px",
fontSize: "2.2rem",
borderRadius: "50%",
}}
>
{eStopAction?.toUpperCase() ?? "Wait for feedback"}
</Button>
</span>
</Stack>
</div>
<>
<Stack flex="auto" gap={1} padding={1.5} position="relative" fullHeight>
<Stack justifyContent="center" alignItems="center" fullWidth fullHeight>
<div className="center">
<Stack
direction="column-reverse"
justifyContent="center"
alignItems="center"
overflow="hidden"
flexGrow={0}
gap={1.5}
>
{statusMessage && (
<Typography variant="caption" noWrap>
{statusMessage}
</Typography>
)}
<span>
<Button
className={classes.button}
variant="contained"
disabled={!canEStop}
onClick={eStopClicked}
data-testid="call-service-button"
style={{
minWidth: "150px",
minHeight: "150px",
fontSize: "2.2rem",
borderRadius: "50%",
}}
>
{eStopAction?.toUpperCase() ?? "Wait for feedback"}
</Button>
</span>
</Stack>
</div>
</Stack>
</Stack>
</Stack>
{srvState?.response?.success === false && (
<NotificationModal
onRequestClose={handleRequestCloseNotification}
notification={{
id: "1",
message: "Request Failed",
details: srvState.response.message,
severity: "error",
}}
/>
)}
</>
);
}
16 changes: 11 additions & 5 deletions packages/studio-base/src/panels/Joy/JoyVisual.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,17 +145,23 @@ function JoyVisual(props: JoyVisualProps): JSX.Element {
return (
<div id="container">
<div id="joystick-container">
<svg id="joystick" viewBox="0 0 100 100" aria-label="Joystick" >
<svg id="joystick" viewBox="0 0 100 100" aria-label="Joystick">
<Arrow direction="up" />
<Arrow direction="down" />
<Arrow direction="left" />
<Arrow direction="right" />
<circle ref={joystickRef} cx="50" cy="50" r={joyRadius.toString()} className="joystick-background" />
<circle onMouseDown={handleStart} onTouchStart={handleStart} ref={handleRef} cx="50" cy="50" r="15" className="joystick-handle" />
<g className="joystick-handle-group">
<circle onMouseDown={handleStart} onTouchStart={handleStart} ref={handleRef} cx="50" cy="50" r="15" className="joystick-handle" />
</g>
</svg>
{advanced && (<div id="joystick-position">
<div>({speed?.x.toFixed(2) ?? "0.00"}, {speed?.y.toFixed(2) ?? "0.00"})</div>
</div>)}
{advanced && (
<div id="joystick-position">
<div>
({speed?.x.toFixed(2) ?? "0.00"}, {speed?.y.toFixed(2) ?? "0.00"})
</div>
</div>
)}
</div>
{advanced && (
<div id="controls">
Expand Down
44 changes: 16 additions & 28 deletions packages/studio-base/src/panels/Joy/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
}

body {
--joystick-color: #888;
--joystick-head-color: #f64;
--joystick-color: #777;
--joystick-color-augmented: #444;
--joystick-head-color: #f52;
--joystick-head-color-augmented: #f63;
}

#container {
Expand All @@ -19,16 +21,6 @@ body {
padding: 2em;
}

#toggle-editing {
position: absolute;
top: 1em;
left: 1em;
font-size: 1.5em;
border-radius: 1em;
cursor: pointer;
transition: #3498db 0.3s ease;
}

#joystick-container {
display: flex;
flex-direction: column;
Expand All @@ -45,28 +37,24 @@ body {

.joystick-background {
fill: var(--joystick-color);
animation: animateCircle 2s infinite;
stroke-width: 2;
stroke: green;
}

.joystick-handle-group {
transform-origin: 50% 50%;
animation: scalingAnimation 2s infinite ease-in-out;
}

/* App.css */
@keyframes animateCircle {
@keyframes scalingAnimation {
0% {
stroke: #26b355;
stroke-width: 2;
}
25% {
stroke: #2dd565;
transform: scale(1);
}
50% {
stroke: #2fde69;
stroke-width: 3;
}
75% {
stroke: #2dd565;
transform: scale(1.1);
}
100% {
stroke: #26b355;
transform: scale(1);
}
}

Expand All @@ -76,11 +64,11 @@ body {
}

.joystick-handle:hover {
fill: #ff6d4d;
fill: var(--joystick-head-color-augmented);
}

.joystick-triangle {
fill: var(--joystick-color);
fill: var(--joystick-color-augmented);
}

#joystick-position {
Expand Down
Loading

0 comments on commit 87d59c2

Please sign in to comment.