diff --git a/.github/workflows/build-control-station.yaml b/.github/workflows/build-control-station.yaml new file mode 100644 index 000000000..e22e558fa --- /dev/null +++ b/.github/workflows/build-control-station.yaml @@ -0,0 +1,49 @@ +name: Build control station + +on: + workflow_call: + workflow_dispatch: + pull_request: + paths: + - control-station/** + - common-front/** + +jobs: + build-control-station: + name: 'Build control station' + runs-on: ubuntu-latest + + env: + FRONTEND_DIR: ./control-station + COMMON_DIR: ./common-front + + steps: + - uses: actions/checkout@v4 + with: + sparse-checkout: | + control-station + common-front + + - name: 'Install common front dependencies' + working-directory: '${{env.COMMON_DIR}}' + run: npm install + + - name: 'Build common front' + working-directory: '${{env.COMMON_DIR}}' + run: npm run build + + - name: 'Install control station dependencies' + working-directory: '${{env.FRONTEND_DIR}}' + run: npm install + + - name: 'Build control station' + working-directory: '${{env.FRONTEND_DIR}}' + run: npm run build + + - name: 'Upload build' + uses: actions/upload-artifact@v4 + with: + name: control-station + path: '${{env.FRONTEND_DIR}}/static/*' + retention-days: 3 + compression-level: 9 diff --git a/backend/cmd/config.toml b/backend/cmd/config.toml index 9a75fdc23..8e360f5ee 100644 --- a/backend/cmd/config.toml +++ b/backend/cmd/config.toml @@ -14,7 +14,7 @@ files = "/" boards = ["VCU"] [excel.download] -id="1NyNaAOw_6iWtnCpEg73AtSSFx1fMdhPRmmdOhjgjCZI" +id = "1NyNaAOw_6iWtnCpEg73AtSSFx1fMdhPRmmdOhjgjCZI" name = "ade.xlsx" path = "." diff --git a/backend/internal/excel/utils/units.go b/backend/internal/excel/utils/units.go index 32d9386bd..007f5371d 100644 --- a/backend/internal/excel/utils/units.go +++ b/backend/internal/excel/utils/units.go @@ -34,7 +34,7 @@ func ParseUnits(literal string, globalUnits map[string]Operations) (Units, error ops, ok := globalUnits[parts[0]] if !ok { - return Units{}, fmt.Errorf("units \"%s\" not found in global", parts[0]) + return Units{Name: parts[0], Operations: make(Operations, 0)}, fmt.Errorf("units \"%s\" not found in global", parts[0]) } return Units{ @@ -44,13 +44,13 @@ func ParseUnits(literal string, globalUnits map[string]Operations) (Units, error } if len(parts) != 2 { - return Units{}, fmt.Errorf("units %v can only have 2 parts", parts) + return Units{Name: parts[0], Operations: make(Operations, 0)}, fmt.Errorf("units %v can only have 2 parts", parts) } operations, err := NewOperations(parts[1]) if err != nil { - return Units{}, err + return Units{Name: parts[0], Operations: make(Operations, 0)}, err } return Units{ diff --git a/backend/internal/pod_data/measurement.go b/backend/internal/pod_data/measurement.go index ee4b756e2..b1a5364f0 100644 --- a/backend/internal/pod_data/measurement.go +++ b/backend/internal/pod_data/measurement.go @@ -35,12 +35,7 @@ func getMeasurements(adeMeasurements []ade.Measurement, globalUnits map[string]u func getMeasurement(adeMeas ade.Measurement, globalUnits map[string]utils.Operations) (Measurement, error) { if isNumeric(adeMeas.Type) { - m, err := getNumericMeasurement(adeMeas, globalUnits) - - if err != nil { - return nil, err - } - return m, nil + return getNumericMeasurement(adeMeas, globalUnits) } else if adeMeas.Type == "bool" { return getBooleanMeasurement(adeMeas), nil } else if strings.HasPrefix(adeMeas.Type, "enum") { diff --git a/backend/pkg/http/handlers.go b/backend/pkg/http/handlers.go index 9b915ecec..21bce1cb2 100644 --- a/backend/pkg/http/handlers.go +++ b/backend/pkg/http/handlers.go @@ -5,6 +5,7 @@ import ( "encoding/json" "io" "net/http" + "sync" "time" ) @@ -12,6 +13,7 @@ type handleData struct { name string modTime time.Time data io.ReadSeeker + dataMx *sync.Mutex } func HandleData(name string, data io.ReadSeeker) *handleData { @@ -19,6 +21,7 @@ func HandleData(name string, data io.ReadSeeker) *handleData { name: name, modTime: time.Now(), data: data, + dataMx: new(sync.Mutex), } } @@ -39,6 +42,8 @@ func (handle *handleData) ServeHTTP(writer http.ResponseWriter, request *http.Re writer.Header().Set("Cache-Control", "no-cache") writer.Header().Set("Pragma", "no-cache") writer.Header().Set("Access-Control-Allow-Origin", "*") + handle.dataMx.Lock() + defer handle.dataMx.Unlock() http.ServeContent(writer, request, handle.name, handle.modTime, handle.data) } diff --git a/common-front/lib/adapters/PodData.ts b/common-front/lib/adapters/PodData.ts index bb415b6a9..697ef6392 100644 --- a/common-front/lib/adapters/PodData.ts +++ b/common-front/lib/adapters/PodData.ts @@ -27,7 +27,10 @@ export type MeasurementAdapter = export type NumericMeasurementAdapter = Omit; export type BooleanMeasurementAdapter = Omit; -export type EnumMeasurementAdapter = Omit; +export type EnumMeasurementAdapter = { options: string[] } & Omit< + EnumMeasurement, + 'value' +>; export function createPodDataFromAdapter(adapter: PodDataAdapter): PodData { const boards: Board[] = Object.values(adapter.boards).map( @@ -117,7 +120,7 @@ export function getEnumMeasurement( id: id, name: adapter.name, type: 'enum', - value: 'Default', + value: adapter.options[0], }; } diff --git a/common-front/lib/components/Caret/Caret.module.scss b/common-front/lib/components/Caret/Caret.module.scss index e856b3c18..f4fa75397 100644 --- a/common-front/lib/components/Caret/Caret.module.scss +++ b/common-front/lib/components/Caret/Caret.module.scss @@ -1,6 +1,4 @@ .caretWrapper { - width: min-content; - height: min-content; display: flex; justify-content: center; align-items: center; diff --git a/common-front/lib/components/Caret/Caret.tsx b/common-front/lib/components/Caret/Caret.tsx index 04b839b00..7c6f3eaf2 100644 --- a/common-front/lib/components/Caret/Caret.tsx +++ b/common-front/lib/components/Caret/Caret.tsx @@ -1,17 +1,17 @@ -import styles from "./Caret.module.scss"; -import { BsFillCaretRightFill } from "react-icons/bs"; +import styles from './Caret.module.scss'; +import { BsFillCaretRightFill } from 'react-icons/bs'; type Props = { isOpen: boolean; onClick?: () => void; className?: string; }; -export const Caret = ({ isOpen, onClick, className = "" }: Props) => { +export const Caret = ({ isOpen, onClick, className = '' }: Props) => { return (
{}
diff --git a/common-front/lib/components/ColorfulChart/ColorfulChart.tsx b/common-front/lib/components/ColorfulChart/ColorfulChart.tsx index 615a21df9..d4b8b6012 100644 --- a/common-front/lib/components/ColorfulChart/ColorfulChart.tsx +++ b/common-front/lib/components/ColorfulChart/ColorfulChart.tsx @@ -1,26 +1,30 @@ -import styles from "./ColorfulChart.module.scss"; -import { Legend } from "./Legend/Legend"; -import { Title } from "./Title/Title"; -import { LinesChart } from "../LinesChart/LinesChart"; -import { LineDescription } from "../LinesChart/types"; -import { useMemo } from "react"; +import styles from './ColorfulChart.module.scss'; +import { Legend } from './Legend/Legend'; +import { Title } from './Title/Title'; +import { LinesChart } from '../LinesChart/LinesChart'; +import { LineDescription } from '../LinesChart/types'; +import { useMemo } from 'react'; -const palette = ["#EE8735", "#51C6EB", "#7BEE35", "#e469ca"]; +const palette = ['#EE8735', '#51C6EB', '#7BEE35', '#e469ca']; type Props = { className?: string; - title: string; + title?: string; items: LineDescription[]; length: number; height?: string; + showLegend?: boolean; + showTitle?: boolean; }; export const ColorfulChart = ({ - className = "", - title, + className = '', + title = '', items, length, - height = "8rem", + height = '8rem', + showLegend = false, + showTitle = false, }: Props) => { const itemsWithPalette = useMemo( () => @@ -33,7 +37,7 @@ export const ColorfulChart = ({ return (
- + {showTitle && <Title title={title} />} <div className={styles.body}> <LinesChart height={height} @@ -42,7 +46,7 @@ export const ColorfulChart = ({ items={itemsWithPalette} length={length} /> - <Legend items={itemsWithPalette} /> + {showLegend && <Legend items={itemsWithPalette} />} </div> </div> ); diff --git a/common-front/lib/components/ColorfulChart/Legend/Legend.tsx b/common-front/lib/components/ColorfulChart/Legend/Legend.tsx index 282d34cf0..ae06aa720 100644 --- a/common-front/lib/components/ColorfulChart/Legend/Legend.tsx +++ b/common-front/lib/components/ColorfulChart/Legend/Legend.tsx @@ -1,6 +1,6 @@ -import styles from "./Legend.module.scss"; -import { LegendItem } from "./LegendItem/LegendItem"; -import { LineDescription } from "../../LinesChart/types"; +import styles from './Legend.module.scss'; +import { LegendItem } from './LegendItem/LegendItem'; +import { LineDescription } from '../../LinesChart/types'; type Props = { items: LineDescription[]; @@ -14,8 +14,8 @@ export const Legend = ({ items }: Props) => { <LegendItem key={item.id} name={item.name} - units={"A"} - value={item.getUpdate()} + units={'A'} + getValue={item.getUpdate} color={item.color} /> ); diff --git a/common-front/lib/components/ColorfulChart/Legend/LegendItem/LegendItem.tsx b/common-front/lib/components/ColorfulChart/Legend/LegendItem/LegendItem.tsx index 64f26b9bd..da00e25e3 100644 --- a/common-front/lib/components/ColorfulChart/Legend/LegendItem/LegendItem.tsx +++ b/common-front/lib/components/ColorfulChart/Legend/LegendItem/LegendItem.tsx @@ -1,19 +1,22 @@ -import styles from "./LegendItem.module.scss"; +import { useState } from 'react'; +import { useGlobalTicker } from '../../../../services'; +import styles from './LegendItem.module.scss'; type Props = { name: string; - value: number; + getValue: () => number; units: string; color: string; }; -export const LegendItem = ({ name, units, value, color }: Props) => { +export const LegendItem = ({ name, units, getValue, color }: Props) => { + const [value, setValue] = useState(getValue()); + + useGlobalTicker(() => setValue(getValue())); + return ( <div className={styles.legendItem}> - <div - className={styles.name} - style={{ backgroundColor: color }} - > + <div className={styles.name} style={{ backgroundColor: color }}> {name} </div> <div className={styles.value}> diff --git a/common-front/lib/components/FormComponents/Button/Button.module.scss b/common-front/lib/components/FormComponents/Button/Button.module.scss index f333cf940..48c6ac6d1 100644 --- a/common-front/lib/components/FormComponents/Button/Button.module.scss +++ b/common-front/lib/components/FormComponents/Button/Button.module.scss @@ -1,3 +1,5 @@ +@use '../../../styles/styles.scss'; + .buttonWrapper { flex: 1 1 0; display: flex; @@ -6,12 +8,12 @@ padding: 0.5rem; text-align: center; - border-radius: 0.5rem; - background-color: var(--tertiary-60); + border-radius: styles.$normal-border-radius; + background-color: styles.$orange; color: white; user-select: none; - filter: var(--shadow); + @include styles.shadow; &.enabled { cursor: pointer; @@ -26,4 +28,9 @@ .label { overflow: hidden; text-overflow: ellipsis; + font-size: 1rem; +} + +.icon { + width: 75%; } diff --git a/common-front/lib/components/FormComponents/Button/Button.tsx b/common-front/lib/components/FormComponents/Button/Button.tsx index a1a56efb7..ec372d5f5 100644 --- a/common-front/lib/components/FormComponents/Button/Button.tsx +++ b/common-front/lib/components/FormComponents/Button/Button.tsx @@ -1,21 +1,23 @@ -import styles from "./Button.module.scss"; -import { animated, useSpring } from "@react-spring/web"; -import { lightenHSL } from "../../../color"; +import styles from './Button.module.scss'; +import { animated, useSpring } from '@react-spring/web'; +import { lightenHSL } from '../../..'; type Props = { - label: string; - onClick: (ev: React.MouseEvent) => void; + label?: string; + icon?: string; + onClick?: (ev: React.MouseEvent) => void; disabled?: boolean; color?: string; className?: string; }; export const Button = ({ - label, - color = "hsl(29, 88%, 57%)", - onClick, - disabled, - className = "", + label = undefined, + icon = undefined, + color = 'hsl(29, 88%, 57%)', + onClick = () => {}, + disabled = false, + className = '', }: Props) => { const [springs, api] = useSpring(() => ({ from: { backgroundColor: color }, @@ -55,6 +57,7 @@ export const Button = ({ }) } > + {icon && <img src={icon} alt="icon" className={styles.icon} />} <span className={styles.label}>{label}</span> </animated.div> ); diff --git a/common-front/lib/components/FormComponents/CheckBox/CheckBox.module.scss b/common-front/lib/components/FormComponents/CheckBox/CheckBox.module.scss index ddec8d4b0..81b4c64be 100644 --- a/common-front/lib/components/FormComponents/CheckBox/CheckBox.module.scss +++ b/common-front/lib/components/FormComponents/CheckBox/CheckBox.module.scss @@ -1,4 +1,7 @@ -.checkbox { +@use '../../../styles/styles.scss'; + +#checkBox { width: 1rem; height: 1rem; + border: 1px solid styles.$alternate-text-color; } diff --git a/common-front/lib/components/FormComponents/CheckBox/CheckBox.tsx b/common-front/lib/components/FormComponents/CheckBox/CheckBox.tsx index 6ba745580..014a6c7a8 100644 --- a/common-front/lib/components/FormComponents/CheckBox/CheckBox.tsx +++ b/common-front/lib/components/FormComponents/CheckBox/CheckBox.tsx @@ -1,26 +1,25 @@ -import { BooleanInputData } from "../../.."; -import styles from "./CheckBox.module.scss"; -import { ChangeEvent, useState } from "react"; +import styles from './CheckBox.module.scss'; +import { ChangeEvent, useState } from 'react'; -type Props = BooleanInputData & { - isRequired?: boolean; - onChange?: (value: boolean) => void; +type Props = { + isRequired: boolean; + onChange: (value: boolean) => void; disabled?: boolean; + initialValue?: boolean; color?: string; }; export const CheckBox = ({ - value, - onChange = () => {}, + onChange, disabled = false, - isRequired = true, - color = "blue", + isRequired, + initialValue = false, + color, }: Props) => { - const [checked, setChecked] = useState(value); + const [checked, setChecked] = useState(initialValue); return ( <input - className={styles.checkbox} type="checkbox" disabled={disabled} style={{ accentColor: color }} diff --git a/common-front/lib/components/FormComponents/Dropdown/Dropdown.module.scss b/common-front/lib/components/FormComponents/Dropdown/Dropdown.module.scss index ae601ebb0..9f1a7af88 100644 --- a/common-front/lib/components/FormComponents/Dropdown/Dropdown.module.scss +++ b/common-front/lib/components/FormComponents/Dropdown/Dropdown.module.scss @@ -1,6 +1,9 @@ +@use '../../../styles/styles.scss'; + .select { width: 100%; - padding: 0.4rem 0.5rem; - border-radius: 0.4rem; - font-size: 0.9rem; + padding: 0.5rem; +} + +option { } diff --git a/common-front/lib/components/FormComponents/Dropdown/Dropdown.tsx b/common-front/lib/components/FormComponents/Dropdown/Dropdown.tsx index 045d75a21..7b4754b6d 100644 --- a/common-front/lib/components/FormComponents/Dropdown/Dropdown.tsx +++ b/common-front/lib/components/FormComponents/Dropdown/Dropdown.tsx @@ -1,11 +1,12 @@ -import { EnumInputData } from "../../.."; -import styles from "./Dropdown.module.scss"; +import styles from './Dropdown.module.scss'; -type Props = EnumInputData & { - onChange?: (v: string) => void; +type Props = { + value?: string; + options: string[]; + onChange: (newValue: string) => void; }; -export const Dropdown = ({ value, options, onChange = () => {} }: Props) => { +export const Dropdown = ({ value, options, onChange }: Props) => { return ( <select name="" @@ -15,10 +16,7 @@ export const Dropdown = ({ value, options, onChange = () => {} }: Props) => { > {options.map((option, index) => { return ( - <option - key={index} - value={option} - > + <option key={index} value={option}> {option} </option> ); diff --git a/common-front/lib/components/FormComponents/ExpandablePairs/ExpandablePairs.module.scss b/common-front/lib/components/FormComponents/ExpandablePairs/ExpandablePairs.module.scss deleted file mode 100644 index e03f60f77..000000000 --- a/common-front/lib/components/FormComponents/ExpandablePairs/ExpandablePairs.module.scss +++ /dev/null @@ -1,12 +0,0 @@ -.expandablePairs { - display: flex; - flex-direction: column; - align-items: stretch; - gap: 0.5rem; - color: var(--primary-40); - font-size: 0.8rem; -} - -.addBtn { - grid-column: 1 / span 2; -} diff --git a/common-front/lib/components/FormComponents/ExpandablePairs/ExpandablePairs.tsx b/common-front/lib/components/FormComponents/ExpandablePairs/ExpandablePairs.tsx deleted file mode 100644 index 9f8142cb3..000000000 --- a/common-front/lib/components/FormComponents/ExpandablePairs/ExpandablePairs.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import { useEffect, useReducer } from "react"; -import styles from "./ExpandablePairs.module.scss"; -import { Pair, PairType } from "./Pair/Pair"; -import { Button } from ".."; -import { nanoid } from "nanoid"; - -type Props = { - leftColumnName: string; - rightColumnName: string; - value: PairType[]; - onChange: (v: PairType[]) => void; -}; - -type State = { id: string; pair: PairType }[]; - -type Action = AddPair | UpdatePair | RemovePair; - -type AddPair = { - type: "add_pair"; -}; - -type UpdatePair = { - type: "update_pair"; - payload: { id: string; newValue: { index: number; value: number | null } }; -}; - -type RemovePair = { - type: "remove_pair"; - payload: string; -}; - -function reducer(state: State, action: Action) { - switch (action.type) { - case "add_pair": - return [...state, { id: nanoid(), pair: [null, null] as const }]; - case "update_pair": - return state.map((item) => { - if (item.id == action.payload.id) { - if (action.payload.newValue.index == 0) { - return { - id: item.id, - pair: [ - action.payload.newValue.value, - item.pair[1], - ] as const, - }; - } else { - return { - id: item.id, - pair: [ - item.pair[0], - action.payload.newValue.value, - ] as const, - }; - } - } - - return item; - }); - case "remove_pair": - return state.filter((item) => item.id != action.payload); - } -} - -export const ExpandablePairs = ({ - leftColumnName, - rightColumnName, - value, - onChange, -}: Props) => { - const [items, dispatch] = useReducer(reducer, value, (value) => - value.map((pair) => ({ id: nanoid(), pair: pair })) - ); - - useEffect(() => { - onChange(items.map((item) => item.pair)); - }, [items]); - - return ( - <div className={styles.expandablePairs}> - <div className={styles.name}>{leftColumnName}</div> - <div className={styles.name}>{rightColumnName}</div> - {items.map((item) => ( - <Pair - key={item.id} - showRemove={items.length > 1} - initialPair={item.pair} - onChange={(v) => - dispatch({ - type: "update_pair", - payload: { id: item.id, newValue: v }, - }) - } - onRemove={() => - dispatch({ type: "remove_pair", payload: item.id }) - } - /> - ))} - <Button - className={styles.addBtn} - label="Add row" - onClick={() => dispatch({ type: "add_pair" })} - /> - </div> - ); -}; diff --git a/common-front/lib/components/FormComponents/ExpandablePairs/Pair/Pair.module.scss b/common-front/lib/components/FormComponents/ExpandablePairs/Pair/Pair.module.scss deleted file mode 100644 index c138da69e..000000000 --- a/common-front/lib/components/FormComponents/ExpandablePairs/Pair/Pair.module.scss +++ /dev/null @@ -1,5 +0,0 @@ -.pair { - display: grid; - grid-template-columns: min(1fr, 4rem) min(1fr, 4rem) auto; - gap: 0.5rem; -} diff --git a/common-front/lib/components/FormComponents/ExpandablePairs/Pair/Pair.tsx b/common-front/lib/components/FormComponents/ExpandablePairs/Pair/Pair.tsx deleted file mode 100644 index e2e423a07..000000000 --- a/common-front/lib/components/FormComponents/ExpandablePairs/Pair/Pair.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import styles from "./Pair.module.scss"; -import { Button, NumericInput } from "../.."; - -export type PairType = readonly [number | null, number | null]; - -type Props = { - initialPair: PairType; - showRemove: boolean; - onChange: (v: { index: number; value: number | null }) => void; - onRemove: () => void; -}; - -export const Pair = ({ - initialPair, - onChange, - showRemove, - onRemove, -}: Props) => { - return ( - <div className={styles.pair}> - <NumericInput - key={0} - value={initialPair[0]} - onChange={(v) => onChange({ index: 0, value: v })} - className={styles.input} - /> - <NumericInput - key={1} - value={initialPair[1]} - onChange={(v) => onChange({ index: 1, value: v })} - className={styles.input} - /> - {showRemove && ( - <Button - label="Remove" - onClick={onRemove} - /> - )} - </div> - ); -}; diff --git a/common-front/lib/components/FormComponents/NumericInput/NumericInput.tsx b/common-front/lib/components/FormComponents/NumericInput/NumericInput.tsx index de62fee68..dda792a44 100644 --- a/common-front/lib/components/FormComponents/NumericInput/NumericInput.tsx +++ b/common-front/lib/components/FormComponents/NumericInput/NumericInput.tsx @@ -1,6 +1,14 @@ -import { useEffect, useState } from "react"; import { TextInput } from "../TextInput/TextInput"; +type Props = { + required: boolean; + defaultValue: number | string; + placeholder: string; + disabled: boolean; + isValid: boolean; + onChange: (value: string) => void; +}; + function getUnion(arr: string[]): string { return arr.map((value) => `(?:${value})`).join("|"); } @@ -15,39 +23,20 @@ const numericInputKeyRegex = (() => { return new RegExp(regexp); })(); -type Props = { - value: number | null; - required?: boolean; - defaultValue?: number | string; - placeholder?: string; - disabled?: boolean; - isValid?: boolean; - className?: string; - onChange?: (value: number | null) => void; -}; - export const NumericInput = ({ - value, - onChange = () => {}, - required = true, - disabled = false, - isValid = true, - placeholder = "Enter number", - className, + onChange, + required, + disabled, + isValid, + placeholder, + defaultValue, }: Props) => { - const [number, setNumber] = useState<number | null>(value); - - useEffect(() => { - onChange(number); - }, [number]); - return ( <TextInput - className={className} - defaultValue={value ?? ""} - isValid={isValid && number !== null} + isValid={isValid} disabled={disabled} placeholder={placeholder} + defaultValue={defaultValue} required={required} onKeyDown={(ev) => { if (!numericInputKeyRegex.test(ev.key)) { @@ -56,16 +45,8 @@ export const NumericInput = ({ } }} onChange={(ev) => { - if (ev.target.value == "") { - setNumber(null); - } else if (isNumber(ev.target.value)) { - setNumber(parseFloat(ev.target.value)); - } + onChange(ev.target.value); }} /> ); }; - -function isNumber(str: string): boolean { - return /^-?\d+(?:\.\d+)?$/.test(str); -} diff --git a/common-front/lib/components/FormComponents/TextInput/TextInput.module.scss b/common-front/lib/components/FormComponents/TextInput/TextInput.module.scss index a3c58f6e3..067c52db5 100644 --- a/common-front/lib/components/FormComponents/TextInput/TextInput.module.scss +++ b/common-front/lib/components/FormComponents/TextInput/TextInput.module.scss @@ -1,18 +1,16 @@ +@use '../../../styles/styles.scss'; + .textInput { - all: unset; flex: 1 1 0; min-width: 5rem; - padding: 0.4rem 0.5rem; - border-radius: 0.4rem; + padding: 0.5rem 0.8rem; + border-radius: styles.$normal-border-radius; font: inherit; transition: border-color 0.1s linear; - border-width: 1px; - border-style: solid; - font-size: 0.9rem; } .valid { - border-color: hsla(112, 100%, 72%, 1); + border-color: #84e067; } .invalid { diff --git a/common-front/lib/components/FormComponents/TextInput/TextInput.tsx b/common-front/lib/components/FormComponents/TextInput/TextInput.tsx index 2716231e5..105770e98 100644 --- a/common-front/lib/components/FormComponents/TextInput/TextInput.tsx +++ b/common-front/lib/components/FormComponents/TextInput/TextInput.tsx @@ -1,18 +1,16 @@ -import styles from "./TextInput.module.scss"; +import styles from './TextInput.module.scss'; type Props = { isValid: boolean } & React.InputHTMLAttributes<HTMLInputElement>; export const TextInput = ({ isValid, ...props }: Props) => { return ( <input - {...props} type="text" name="" className={`${styles.textInput} ${ isValid ? styles.valid : styles.invalid - } ${props.disabled ? styles.disabled : ""} ${ - props.className ?? "" - }`} + } ${props.disabled ? styles.disabled : ''}`} + {...props} /> ); }; diff --git a/common-front/lib/components/FormComponents/index.ts b/common-front/lib/components/FormComponents/index.ts index e68f2acce..e26c58a2c 100644 --- a/common-front/lib/components/FormComponents/index.ts +++ b/common-front/lib/components/FormComponents/index.ts @@ -1,7 +1,6 @@ -export * from "./Button/Button"; -export * from "./CheckBox/CheckBox"; -export * from "./Dropdown/Dropdown"; -export * from "./FileInput/FileInput"; -export * from "./NumericInput/NumericInput"; -export * from "./TextInput/TextInput"; -export * from "./ExpandablePairs/ExpandablePairs"; +export * from './Button/Button'; +export * from './CheckBox/CheckBox'; +export * from './Dropdown/Dropdown'; +export * from './FileInput/FileInput'; +export * from './NumericInput/NumericInput'; +export * from './TextInput/TextInput'; diff --git a/common-front/lib/components/LinesChart/types.ts b/common-front/lib/components/LinesChart/types.ts index 68d14e137..1bf73cc38 100644 --- a/common-front/lib/components/LinesChart/types.ts +++ b/common-front/lib/components/LinesChart/types.ts @@ -1,9 +1,10 @@ -import { RangeArray } from "./RangeArray"; +import { RangeArray } from './RangeArray'; export type LineDescription = { readonly id: string; readonly name: string; readonly range: [number | null, number | null]; + readonly warningRange: [number | null, number | null]; readonly color: string; readonly getUpdate: () => number; }; diff --git a/common-front/lib/components/LinesChart/useLines.ts b/common-front/lib/components/LinesChart/useLines.ts index bb9d23264..3808d60f1 100644 --- a/common-front/lib/components/LinesChart/useLines.ts +++ b/common-front/lib/components/LinesChart/useLines.ts @@ -1,8 +1,8 @@ -import { useCallback, useLayoutEffect, useMemo, useRef, useState } from "react"; -import { dataToPath } from "./path"; -import { RangeArray } from "./RangeArray"; -import { useGlobalTicker } from "../../services/GlobalTicker/useGlobalTicker"; -import { Line, LineDescription } from "./types"; +import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react'; +import { dataToPath } from './path'; +import { RangeArray } from './RangeArray'; +import { useGlobalTicker } from '../../services/GlobalTicker/useGlobalTicker'; +import { Line, LineDescription } from './types'; function getLargestRangeFromPartial( ranges: ReadonlyArray<readonly [number | null, number | null]> @@ -24,7 +24,7 @@ export function useLines( const initialLargestRange = useMemo( () => getLargestRangeFromPartial( - lineDescriptions.map((line) => line.range) + lineDescriptions.map((line) => line.warningRange) ), [] ); @@ -60,7 +60,7 @@ export function useLines( viewBoxHeight ); - line.ref.setAttribute("d", path); + line.ref.setAttribute('d', path); }); }, []); @@ -131,26 +131,27 @@ function createLines( name: description.name, ref: pathElement, range: description.range, + warningRange: description.warningRange, getUpdate: () => description.getUpdate(), data: lines.find((line) => description.id == line.id)?.data ?? new RangeArray([], length), - color: "red", + color: 'red', }; }); } function createPathElement(color: string, path: string): SVGPathElement { const pathElement = document.createElementNS( - "http://www.w3.org/2000/svg", - "path" + 'http://www.w3.org/2000/svg', + 'path' ); - pathElement.setAttribute("vector-effect", "non-scaling-stroke"); - pathElement.setAttribute("stroke", color); - pathElement.setAttribute("stroke-width", "3"); - pathElement.setAttribute("stroke-linejoin", "round"); - pathElement.setAttribute("d", path); + pathElement.setAttribute('vector-effect', 'non-scaling-stroke'); + pathElement.setAttribute('stroke', color); + pathElement.setAttribute('stroke-width', '3'); + pathElement.setAttribute('stroke-linejoin', 'round'); + pathElement.setAttribute('d', path); return pathElement; } diff --git a/common-front/lib/components/Logger/Logger.module.scss b/common-front/lib/components/Logger/Logger.module.scss index 85f246bd2..593c2e6c9 100644 --- a/common-front/lib/components/Logger/Logger.module.scss +++ b/common-front/lib/components/Logger/Logger.module.scss @@ -4,21 +4,22 @@ flex-direction: row; justify-content: space-between; align-items: center; - gap: 1rem; + width: 100%; } .state { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; + font-size: 1.1rem; } .buttons { - width: 10rem; display: flex; flex-direction: row; align-items: center; gap: 1rem; + width: fit-content; > * { min-width: 0; diff --git a/common-front/lib/components/MessagesContainer/Messages/Messages.module.scss b/common-front/lib/components/MessagesContainer/Messages/Messages.module.scss index 9fe5b4f88..c1481b2bd 100644 --- a/common-front/lib/components/MessagesContainer/Messages/Messages.module.scss +++ b/common-front/lib/components/MessagesContainer/Messages/Messages.module.scss @@ -1,4 +1,5 @@ .messagesWrapper { + height: 100%; flex: 1 1 0; display: flex; flex-direction: column; diff --git a/common-front/lib/components/Orders/BoardOrders/BoardOrders.module.scss b/common-front/lib/components/Orders/BoardOrders/BoardOrders.module.scss new file mode 100644 index 000000000..5a17684e4 --- /dev/null +++ b/common-front/lib/components/Orders/BoardOrders/BoardOrders.module.scss @@ -0,0 +1,28 @@ +@use '../../../styles/styles.scss'; +.boardOrders { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.name { + display: flex; + gap: 0.5rem; + font-family: Inter; + font-weight: 500; + color: hsl(29, 88%, 70%); + cursor: pointer; +} + +.orders, +.stateOrders { + display: flex; + flex-direction: column; + gap: 0.5rem; + + .title { + color: rgb(145, 145, 145); + font-weight: 500; + font-size: 0.8rem; + } +} diff --git a/common-front/lib/components/Orders/BoardOrders/BoardOrders.tsx b/common-front/lib/components/Orders/BoardOrders/BoardOrders.tsx new file mode 100644 index 000000000..fea2931bd --- /dev/null +++ b/common-front/lib/components/Orders/BoardOrders/BoardOrders.tsx @@ -0,0 +1,60 @@ +import { BoardOrders, Caret } from '../../..'; +import styles from './BoardOrders.module.scss'; +import { OrderForm } from './OrderForm/OrderForm'; +import { useState } from 'react'; + +type Props = { + boardOrders: BoardOrders; + alwaysShowStateOrders: boolean; +}; + +export const BoardOrdersView = ({ + boardOrders, + alwaysShowStateOrders, +}: Props) => { + const [isOpen, setIsOpen] = useState(true); + + return ( + <div className={styles.boardOrders}> + <div + className={styles.name} + onClick={() => setIsOpen((prev) => !prev)} + > + <Caret isOpen={isOpen} /> + {boardOrders.name} + </div> + + <div + className={styles.orders} + style={{ + display: isOpen ? 'flex' : 'none', + }} + > + <span className={styles.title}>Permanent orders</span> + {boardOrders.orders.map((desc) => { + return <OrderForm key={desc.id} description={desc} />; + })} + </div> + + {boardOrders.stateOrders.length > 0 && + (alwaysShowStateOrders || + boardOrders.stateOrders.some((item) => item.enabled)) && ( + <div className={styles.stateOrders}> + <span className={styles.title}>State orders</span> + {boardOrders.stateOrders.map((desc) => { + if (alwaysShowStateOrders || desc.enabled) { + return ( + <OrderForm + key={desc.id} + description={desc} + /> + ); + } else { + return false; + } + })} + </div> + )} + </div> + ); +}; diff --git a/common-front/lib/components/Orders/BoardOrders/OrderForm/Fields/Field/Field.module.scss b/common-front/lib/components/Orders/BoardOrders/OrderForm/Fields/Field/Field.module.scss new file mode 100644 index 000000000..33b314fed --- /dev/null +++ b/common-front/lib/components/Orders/BoardOrders/OrderForm/Fields/Field/Field.module.scss @@ -0,0 +1,23 @@ +@use '../../../../../../styles/styles.scss'; + +.fieldWrapper { + width: 100%; + display: flex; + flex-flow: column; + gap: 0.5rem; +} + +.input { + display: flex; + flex-flow: row; + gap: 0.5rem; +} + +.name { + text-overflow: ellipsis; + overflow: hidden; +} + +.disabled { + color: rgb(176, 176, 176); +} diff --git a/common-front/lib/components/Orders/BoardOrders/OrderForm/Fields/Field/Field.tsx b/common-front/lib/components/Orders/BoardOrders/OrderForm/Fields/Field/Field.tsx new file mode 100644 index 000000000..d231726b9 --- /dev/null +++ b/common-front/lib/components/Orders/BoardOrders/OrderForm/Fields/Field/Field.tsx @@ -0,0 +1,80 @@ +import styles from './Field.module.scss'; +import { + CheckBox, + Dropdown, + NumericType, + NumericInput, +} from '../../../../../..'; +import { isNumberValid } from './validation'; +import { FormField } from '../../form'; + +type Props = { + name: string; + field: FormField; + onChange: (newValue: boolean | string | number, isValid: boolean) => void; + changeEnabled: (isEnabled: boolean) => void; +}; + +export const Field = ({ name, field, onChange, changeEnabled }: Props) => { + function handleTextInputChange( + value: string, + type: NumericType, + range: [number | null, number | null] + ) { + const isValid = isNumberValid(value, type, range); + onChange(Number.parseFloat(value), isValid); + } + + return ( + <div + className={`${styles.fieldWrapper} ${ + !field.isEnabled ? styles.disabled : '' + }`} + > + <div className={styles.name}>{name}</div> + <div className={styles.input}> + {field.kind == 'numeric' ? ( + <NumericInput + required={field.isEnabled} + disabled={!field.isEnabled} + isValid={field.isValid} + placeholder={`${field.type}...`} + defaultValue={ + !field.isValid ? '' : (field.value as number) + } + onChange={(value) => + handleTextInputChange( + value, + field.type, + field.safeRange + ) + } + /> + ) : field.kind == 'boolean' ? ( + <CheckBox + isRequired={field.isEnabled} + disabled={!field.isEnabled} + onChange={(value: boolean) => { + onChange(value, true); + }} + /> + ) : ( + <Dropdown + value={field.value as string} + options={field.options} + onChange={(newValue) => { + onChange(newValue, true); + }} + /> + )} + + <CheckBox + color="orange" + isRequired={true} + onChange={changeEnabled} + initialValue={field.isEnabled} + /> + </div> + </div> + ); +}; diff --git a/common-front/lib/components/Orders/BoardOrders/OrderForm/Fields/Field/validation.ts b/common-front/lib/components/Orders/BoardOrders/OrderForm/Fields/Field/validation.ts new file mode 100644 index 000000000..9ae0aa5a5 --- /dev/null +++ b/common-front/lib/components/Orders/BoardOrders/OrderForm/Fields/Field/validation.ts @@ -0,0 +1,109 @@ +import { + NumericType, + isSignedIntegerType, + isUnsignedIntegerType, +} from '../../../../../..'; + +export function isNumberValid( + valueStr: string, + numberType: NumericType, + range: [number | null, number | null] +): boolean { + if (stringIsNumber(valueStr, numberType)) { + if (isUnsignedIntegerType(numberType)) { + let isValid = true; + if (range[0]) { + isValid &&= Number.parseInt(valueStr) >= range[0]; + } + + if (range[1]) { + isValid &&= Number.parseInt(valueStr) <= range[1]; + } + + return ( + isValid && + checkUnsignedIntegerOverflow( + Number.parseInt(valueStr), + getBits(numberType) + ) + ); + } else if (isSignedIntegerType(numberType)) { + let isValid = true; + if (range[0]) { + isValid &&= Number.parseInt(valueStr) >= range[0]; + } + + if (range[1]) { + isValid &&= Number.parseInt(valueStr) <= range[1]; + } + + return ( + isValid && + checkSignedIntegerOverflow( + Number.parseInt(valueStr), + getBits(numberType) + ) + ); + } else { + let isValid = true; + if (range[0]) { + isValid &&= Number.parseFloat(valueStr) >= range[0]; + } + + if (range[1]) { + isValid &&= Number.parseFloat(valueStr) <= range[1]; + } + + return isValid && checkFloatOverflow(Number.parseFloat(valueStr)); + } + } else { + return false; + } +} + +function stringIsNumber(valueStr: string, numberType: NumericType): boolean { + if (isUnsignedIntegerType(numberType)) { + return /^\d+$/.test(valueStr); + } else if (isSignedIntegerType(numberType)) { + return /^-?\d+$/.test(valueStr); + } else { + return /^-?\d+(?:\.\d+)?$/.test(valueStr); + } +} + +function checkUnsignedIntegerOverflow(value: number, bits: number): boolean { + return value >= 0 && value < 1 << bits; //FIXME: añadir unos +} + +function checkSignedIntegerOverflow(value: number, bits: number): boolean { + return value >= -1 << (bits - 1) && value < 1 << (bits - 1); +} + +function checkFloatOverflow(value: number): boolean { + return !Number.isNaN(value); +} + +function getBits(type: NumericType): number { + switch (type) { + case 'uint8': + return 8; + case 'uint16': + return 16; + case 'uint32': + return 32; + case 'uint64': + return 64; + case 'int8': + return 8; + case 'int16': + return 16; + case 'int32': + return 32; + case 'int64': + return 64; + case 'float32': + return 32; + case 'float64': + return 64; + } +} diff --git a/common-front/lib/components/Orders/BoardOrders/OrderForm/Fields/Fields.module.scss b/common-front/lib/components/Orders/BoardOrders/OrderForm/Fields/Fields.module.scss new file mode 100644 index 000000000..27c1c59f0 --- /dev/null +++ b/common-front/lib/components/Orders/BoardOrders/OrderForm/Fields/Fields.module.scss @@ -0,0 +1,13 @@ +@use '../../../../../styles/styles.scss'; + +.fieldsWrapper { + width: 100%; + padding: 1rem; + border-top: 1px solid styles.$orange; + + display: flex; + flex-direction: column; + gap: 0.5rem; + + overflow-x: auto; +} diff --git a/common-front/lib/components/Orders/BoardOrders/OrderForm/Fields/Fields.tsx b/common-front/lib/components/Orders/BoardOrders/OrderForm/Fields/Fields.tsx new file mode 100644 index 000000000..e2a53d5fe --- /dev/null +++ b/common-front/lib/components/Orders/BoardOrders/OrderForm/Fields/Fields.tsx @@ -0,0 +1,36 @@ +import styles from "./Fields.module.scss"; +import { Field } from "./Field/Field"; +import { FormField } from "../form"; + +type Props = { + fields: FormField[]; + updateField: ( + id: string, + value: boolean | string | number, + isValid: boolean + ) => void; + changeEnable: (id: string, isEnabled: boolean) => void; +}; + +export const Fields = ({ fields, updateField, changeEnable }: Props) => { + return ( + <div className={styles.fieldsWrapper}> + {fields.map((field) => { + return ( + <Field + key={field.id} + name={field.name} + field={field} + onChange={( + newValue: string | number | boolean, + isValid: boolean + ) => { + updateField(field.id, newValue, isValid); + }} + changeEnabled={(value) => changeEnable(field.id, value)} + /> + ); + })} + </div> + ); +}; diff --git a/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Header/Header.module.scss b/common-front/lib/components/Orders/BoardOrders/OrderForm/Header/Header.module.scss similarity index 82% rename from common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Header/Header.module.scss rename to common-front/lib/components/Orders/BoardOrders/OrderForm/Header/Header.module.scss index b6a7bb6fc..dc5470480 100644 --- a/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Header/Header.module.scss +++ b/common-front/lib/components/Orders/BoardOrders/OrderForm/Header/Header.module.scss @@ -1,16 +1,16 @@ -$main: var(--tertiary-60); -$light: var(--tertiary-90); +@use '../../../../../styles/styles.scss'; +$header-color: #ffe4cc; .headerWrapper { flex: 0 0 0; padding: 0.5rem; display: grid; grid-template: - "caret name target button" auto + 'caret name target button' auto / auto 1fr auto 5rem; align-items: center; gap: 0.5rem; - background-color: $light; + background-color: $header-color; cursor: pointer; } @@ -24,7 +24,7 @@ $light: var(--tertiary-90); } .caret > * { - color: $main; + color: styles.$orange; } .visible { @@ -37,7 +37,7 @@ $light: var(--tertiary-90); .name { grid-area: name; - color: $main; + color: styles.$orange; font-weight: 500; overflow: hidden; text-overflow: ellipsis; diff --git a/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Header/Header.tsx b/common-front/lib/components/Orders/BoardOrders/OrderForm/Header/Header.tsx similarity index 67% rename from common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Header/Header.tsx rename to common-front/lib/components/Orders/BoardOrders/OrderForm/Header/Header.tsx index 7ec1f6f27..5714a210d 100644 --- a/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Header/Header.tsx +++ b/common-front/lib/components/Orders/BoardOrders/OrderForm/Header/Header.tsx @@ -1,20 +1,19 @@ -import styles from "./Header.module.scss"; -import { useState, useEffect } from "react"; -import { SpringValue, animated } from "@react-spring/web"; -import { ReactComponent as Target } from "../../../../../../assets/icons/target.svg"; -import { Caret } from "../../../../../Caret/Caret"; -import { Button } from "../../../../.."; +import styles from './Header.module.scss'; +import { useState, useEffect } from 'react'; +import { Button, Caret } from '../../../../..'; +import { SpringValue, animated } from '@react-spring/web'; +import { ReactComponent as Target } from '../../../../../assets/icons/target.svg'; export type HeaderInfo = ToggableHeader | FixedHeader; type ToggableHeader = { - type: "toggable"; + type: 'toggable'; isOpen: boolean; toggleDropdown: () => void; }; type FixedHeader = { - type: "fixed"; + type: 'fixed'; }; type Props = { @@ -22,7 +21,7 @@ type Props = { disabled: boolean; info: HeaderInfo; springs: Record<string, SpringValue>; - onTargetToggle: (state: boolean) => void; + onTargetClick: (state: boolean) => void; onButtonClick: () => void; }; @@ -31,7 +30,7 @@ export const Header = ({ disabled, info, springs, - onTargetToggle: onTargetClick, + onTargetClick, onButtonClick, }: Props) => { const [targetOn, setTargetOn] = useState(false); @@ -43,22 +42,22 @@ export const Header = ({ return ( <animated.div className={styles.headerWrapper} - onClick={info.type == "toggable" ? info.toggleDropdown : () => {}} + onClick={info.type == 'toggable' ? info.toggleDropdown : () => {}} style={{ ...springs, - cursor: info.type == "toggable" ? "pointer" : "auto", + cursor: info.type == 'toggable' ? 'pointer' : 'auto', }} > <Caret - isOpen={info.type == "toggable" ? info.isOpen : false} + isOpen={info.type == 'toggable' ? info.isOpen : false} className={`${styles.caret} ${ - info.type == "toggable" ? styles.visible : styles.hidden + info.type == 'toggable' ? styles.visible : styles.hidden }`} /> <div className={styles.name}>{name}</div> <Target className={`${styles.target} ${ - targetOn ? styles.targetVisible : "" + targetOn ? styles.targetVisible : '' }`} onClick={(ev) => { ev.stopPropagation(); diff --git a/common-front/lib/components/Orders/BoardOrders/OrderForm/OrderForm.module.scss b/common-front/lib/components/Orders/BoardOrders/OrderForm/OrderForm.module.scss new file mode 100644 index 000000000..85a82cae1 --- /dev/null +++ b/common-front/lib/components/Orders/BoardOrders/OrderForm/OrderForm.module.scss @@ -0,0 +1,14 @@ +@use '../../../../styles/styles.scss'; + +.orderFormWrapper { + flex-shrink: 0; + display: flex; + flex-direction: column; + align-items: stretch; + @include styles.code-text; + border: 1px solid styles.$orange; + border-radius: 0.5rem; + overflow: hidden; + + @include styles.shadow; +} diff --git a/common-front/lib/components/Orders/BoardOrders/OrderForm/OrderForm.tsx b/common-front/lib/components/Orders/BoardOrders/OrderForm/OrderForm.tsx new file mode 100644 index 000000000..8e40fc0bb --- /dev/null +++ b/common-front/lib/components/Orders/BoardOrders/OrderForm/OrderForm.tsx @@ -0,0 +1,86 @@ +import styles from './OrderForm.module.scss'; +import { OrderDescription, Order } from '../../../..'; +import { Header, HeaderInfo } from './Header/Header'; +import { Fields } from './Fields/Fields'; +import { useContext, useState } from 'react'; +import { useForm } from './useForm'; +import { useSpring } from '@react-spring/web'; +import { useListenKey } from './useListenKey'; +import { OrderContext } from '../../OrderContext'; +import { FormField } from './form'; + +type Props = { + description: OrderDescription; +}; + +function createOrder(id: number, fields: FormField[]): Order { + return { + id: id, + fields: Object.fromEntries( + fields.map((field) => { + return [ + field.id, + { + value: field.value, + isEnabled: field.isEnabled, + type: field.type, + }, + ]; + }) + ), + }; +} + +export const OrderForm = ({ description }: Props) => { + const sendOrder = useContext(OrderContext); + const { form, updateField, changeEnable } = useForm(description.fields); + const [isOpen, setIsOpen] = useState(false); + const [springs, api] = useSpring(() => ({ + from: { filter: 'brightness(1)' }, + config: { + tension: 600, + }, + })); + + const trySendOrder = () => { + if (form.isValid) { + api.start({ + from: { filter: 'brightness(1.2)' }, + to: { filter: 'brightness(1)' }, + }); + + sendOrder(createOrder(description.id, form.fields)); + } + }; + + const listen = useListenKey(' ', trySendOrder); + + const headerInfo: HeaderInfo = + form.fields.length > 0 + ? { + type: 'toggable', + isOpen: isOpen, + toggleDropdown: () => setIsOpen((prevValue) => !prevValue), + } + : { type: 'fixed' }; + + return ( + <div className={styles.orderFormWrapper}> + <Header + name={description.name} + info={headerInfo} + disabled={!form.isValid} + onTargetClick={listen} + onButtonClick={trySendOrder} + springs={springs} + /> + {isOpen && ( + <Fields + fields={form.fields} + updateField={updateField} + changeEnable={changeEnable} + /> + )} + </div> + ); +}; diff --git a/common-front/lib/components/Orders/BoardOrders/OrderForm/form.ts b/common-front/lib/components/Orders/BoardOrders/OrderForm/form.ts new file mode 100644 index 000000000..9ecc7126f --- /dev/null +++ b/common-front/lib/components/Orders/BoardOrders/OrderForm/form.ts @@ -0,0 +1,102 @@ +import { + BooleanDescription, + EnumDescription, + NumericDescription, + OrderFieldDescription, +} from '../../../..'; + +export type FormField = NumericField | BooleanField | EnumField; + +type AbstractFormField = { + id: string; + isValid: boolean; + isEnabled: boolean; +}; + +export type NumericField = AbstractFormField & + NumericDescription & { + value: number; + }; + +export type BooleanField = AbstractFormField & + BooleanDescription & { + value: boolean; + }; + +export type EnumField = AbstractFormField & + EnumDescription & { + value: string; + }; + +export type Form = { + fields: FormField[]; + isValid: boolean; +}; + +export function areFieldsValid(fields: Array<FormField>): boolean { + return fields.reduce((prevValid, currentField) => { + return ( + prevValid && + ((currentField.isEnabled && currentField.isValid) || + !currentField.isEnabled) + ); + }, true); +} + +export function createForm( + descriptions: Record<string, OrderFieldDescription> +): Form { + const fields = Object.entries(descriptions).map(([_, fieldDescription]) => { + const field = getFormField(fieldDescription); + return field; + }); + + return { fields, isValid: areFieldsValid(fields) }; +} + +function getFormField(desc: OrderFieldDescription): FormField { + if (desc.kind == 'numeric') { + return getNumericFormField(desc); + } else if (desc.kind == 'boolean') { + return getBooleanFormField(desc); + } else { + return getEnumFormField(desc); + } +} + +function getNumericFormField(desc: NumericDescription): NumericField { + return { + id: desc.id, + name: desc.name, + kind: desc.kind, + type: desc.type, + safeRange: desc.safeRange, + warningRange: desc.warningRange, + value: 0, + isValid: false, + isEnabled: true, + }; +} +function getBooleanFormField(desc: BooleanDescription): BooleanField { + return { + id: desc.id, + name: desc.name, + kind: desc.kind, + type: desc.type, + value: false, + isValid: true, + isEnabled: true, + }; +} +function getEnumFormField(desc: EnumDescription): EnumField { + return { + id: desc.id, + name: desc.name, + kind: desc.kind, + type: desc.type, + options: desc.options, + value: desc.options[0], + isValid: true, + isEnabled: true, + }; +} diff --git a/common-front/lib/components/Orders/BoardOrders/OrderForm/useForm.ts b/common-front/lib/components/Orders/BoardOrders/OrderForm/useForm.ts new file mode 100644 index 000000000..ce0249d82 --- /dev/null +++ b/common-front/lib/components/Orders/BoardOrders/OrderForm/useForm.ts @@ -0,0 +1,78 @@ +import { OrderFieldDescription } from '../../../..'; +import { useReducer } from 'react'; +import { Form, areFieldsValid, createForm, FormField } from './form'; + +type Action = UpdateField | ChangeEnable; + +type UpdateField = { + type: 'update_field'; + payload: { + id: string; + isValid: boolean; + value: number | string | boolean; + }; +}; + +type ChangeEnable = { + type: 'change_enable'; + payload: { + id: string; + enable: boolean; + }; +}; + +function reducer(state: Form, action: Action): Form { + switch (action.type) { + case 'update_field': { + const fields: FormField[] = state.fields.map((field) => { + if (field.id == action.payload.id) { + return { + ...field, + value: action.payload.value, + isValid: action.payload.isValid, + }; + } + return field; + }) as FormField[]; + + return { + fields, + isValid: areFieldsValid(fields), + }; + } + + case 'change_enable': { + const fields = state.fields.map((field) => + field.id == action.payload.id + ? { ...field, isEnabled: action.payload.enable } + : field + ); + return { + fields, + isValid: areFieldsValid(fields), + }; + } + } +} + +export function useForm(descriptions: Record<string, OrderFieldDescription>) { + const [form, dispatch] = useReducer(reducer, descriptions, createForm); + + const updateField = ( + id: string, + value: string | boolean | number, + isValid: boolean + ) => + dispatch({ + type: 'update_field', + payload: { id, value, isValid }, + }); + + const changeEnable = (id: string, enable: boolean) => + dispatch({ + type: 'change_enable', + payload: { id, enable }, + }); + + return { form, updateField, changeEnable } as const; +} diff --git a/common-front/lib/components/Orders/BoardOrders/OrderForm/useListenKey.ts b/common-front/lib/components/Orders/BoardOrders/OrderForm/useListenKey.ts new file mode 100644 index 000000000..d4150974f --- /dev/null +++ b/common-front/lib/components/Orders/BoardOrders/OrderForm/useListenKey.ts @@ -0,0 +1,26 @@ +import { useEffect, useState } from "react"; + +export function useListenKey(key: string, callback: () => unknown) { + const [listen, setListen] = useState(false); + + const listener = (ev: KeyboardEvent) => { + if (ev.key == key) { + ev.preventDefault(); + callback(); + } + }; + + useEffect(() => { + if (listen) { + document.addEventListener("keydown", listener); + } + + return () => { + document.removeEventListener("keydown", listener); + }; + }, [listen, key, listener, callback]); + + return (value: boolean) => { + setListen(value); + }; +} diff --git a/common-front/lib/components/Orders/BoardOrdersView/BoardOrdersView.module.scss b/common-front/lib/components/Orders/BoardOrdersView/BoardOrdersView.module.scss deleted file mode 100644 index 281b48066..000000000 --- a/common-front/lib/components/Orders/BoardOrdersView/BoardOrdersView.module.scss +++ /dev/null @@ -1,17 +0,0 @@ -.boardOrdersView { - display: flex; - flex-direction: column; - gap: 0.5rem; -} - -.name { - font-family: Inter; - font-weight: 600; - color: hsl(29, 88%, 70%); -} - -.orders { - display: flex; - flex-direction: column; - gap: 1rem; -} diff --git a/common-front/lib/components/Orders/BoardOrdersView/BoardOrdersView.tsx b/common-front/lib/components/Orders/BoardOrdersView/BoardOrdersView.tsx deleted file mode 100644 index 8fe2c0b9c..000000000 --- a/common-front/lib/components/Orders/BoardOrdersView/BoardOrdersView.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { BoardOrders } from "../../.."; -import styles from "./BoardOrdersView.module.scss"; -import { OrdersList } from "./OrdersList/OrderList"; - -type Props = { - board: BoardOrders; - showName: boolean; -}; - -export const BoardOrdersView = ({ board, showName }: Props) => { - const stateOrders = board.stateOrders.filter((order) => order.enabled); - - return ( - <div className={styles.boardOrdersView}> - {showName && <div className={styles.name}>{board.name}</div>} - <div className={styles.orders}> - {board.orders.length > 0 && ( - <OrdersList - title="Permanent orders" - orders={board.orders} - /> - )} - {stateOrders.length > 0 && ( - <OrdersList - title="State orders" - orders={stateOrders} - /> - )} - </div> - </div> - ); -}; diff --git a/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Field/Field.module.scss b/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Field/Field.module.scss deleted file mode 100644 index 85b92c411..000000000 --- a/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Field/Field.module.scss +++ /dev/null @@ -1,11 +0,0 @@ -.field { - display: flex; - flex-direction: column; - align-items: stretch; - gap: 0.3125rem; -} - -.name { - color: var(--primary-50); - font-size: 0.8rem; -} diff --git a/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Field/Field.tsx b/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Field/Field.tsx deleted file mode 100644 index 990ade6bf..000000000 --- a/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Field/Field.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { FieldEvent, Field as FieldType } from "../../../../../.."; -import styles from "./Field.module.scss"; -import { Input } from "./Input/Input"; - -type Props<T extends FieldType> = { - field: T; - onChange: (ev: FieldEvent) => void; -}; - -export const Field = <T extends FieldType>({ field, onChange }: Props<T>) => { - return ( - <div className={styles.field}> - <div className={styles.name}>{field.name}</div> - <Input - input={{ ...field }} - onChange={(ev) => onChange({ id: field.id, ev: ev })} - /> - </div> - ); -}; diff --git a/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Field/Input/Input.tsx b/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Field/Input/Input.tsx deleted file mode 100644 index 75a2c2af3..000000000 --- a/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Field/Input/Input.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { - CheckBox, - Dropdown, - ExpandablePairs, - NumericInput, -} from "../../../../../.."; -import { Inputs, InputEvent } from "../../../../../../.."; - -type InputData = { - [K in keyof Inputs]: { type: K } & Inputs[K]; -}[keyof Inputs]; - -type Props<I extends InputData> = { - input: I; - onChange: (type: InputEvent) => void; -}; - -export const Input = <I extends InputData>({ input, onChange }: Props<I>) => { - switch (input.type) { - case "numeric": - return ( - <NumericInput - {...input} - placeholder={`${input.placeholder}...`} - onChange={(v) => onChange({ type: "numeric", value: v })} - /> - ); - case "boolean": - return ( - <CheckBox - {...input} - onChange={(v) => onChange({ type: "boolean", value: v })} - /> - ); - case "enum": - return ( - <Dropdown - {...input} - onChange={(v) => onChange({ type: "enum", value: v })} - /> - ); - case "expandablePairs": - return ( - <ExpandablePairs - {...input} - leftColumnName="Position" - rightColumnName="Velocity" - onChange={(v) => console.log(v)} - /> - ); - } -}; diff --git a/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Form.module.scss b/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Form.module.scss deleted file mode 100644 index 9bf93496b..000000000 --- a/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Form.module.scss +++ /dev/null @@ -1,26 +0,0 @@ -$main: var(--tertiary-60); - -.formWrapper { - flex-shrink: 0; - display: flex; - flex-direction: column; - align-items: stretch; - font-family: var(--font-mono); - border: 1px solid $main; - border-radius: 0.5rem; - overflow: hidden; - - filter: var(--shadow); -} - -.fields { - padding: 1rem; - border-top: 1px solid $main; - - display: flex; - flex-direction: column; - gap: 1rem; - - overflow-x: auto; - -} \ No newline at end of file diff --git a/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Form.tsx b/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Form.tsx deleted file mode 100644 index fbec363ca..000000000 --- a/common-front/lib/components/Orders/BoardOrdersView/OrdersList/Form/Form.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { Field } from "./Field/Field"; -import styles from "./Form.module.scss"; -import { Header, HeaderInfo } from "./Header/Header"; -import { useSpring } from "@react-spring/web"; -import { useListenKey } from "../../../../../hooks/useListenKey"; -import { useState } from "react"; -import { useForm, Form as FormType } from "../../../../.."; - -type Props = { - initialForm: Omit<FormType, "isValid">; - onSubmit: (form: FormType) => void; -}; - -export const Form = ({ initialForm, onSubmit }: Props) => { - const [listen, setListen] = useState(false); - const [form, handleEvent] = useForm(initialForm); - const [isOpen, setIsOpen] = useState(false); - const [springs, api] = useSpring(() => ({ - from: { filter: "brightness(1)" }, - config: { - tension: 600, - }, - })); - - function blinkHeader() { - api.start({ - from: { filter: "brightness(1.2)" }, - to: { filter: "brightness(1)" }, - }); - } - - function trySubmit() { - if (form.isValid) { - blinkHeader(); - onSubmit(form); - } - } - - useListenKey( - " ", - () => { - trySubmit(); - }, - listen - ); - - const headerInfo: HeaderInfo = - form.fields.length > 0 - ? { - type: "toggable", - isOpen: isOpen, - toggleDropdown: () => setIsOpen((prevValue) => !prevValue), - } - : { type: "fixed" }; - - return ( - <div className={styles.formWrapper}> - <Header - name={form.name} - info={headerInfo} - disabled={!form.isValid} - onTargetToggle={(state) => setListen(state)} - onButtonClick={() => trySubmit()} - springs={springs} - /> - {isOpen && form.fields.length > 0 && ( - <div className={styles.fields}> - {form.fields.map((field) => ( - <Field - key={field.id} - field={field} - onChange={handleEvent} - /> - ))} - </div> - )} - </div> - ); -}; diff --git a/common-front/lib/components/Orders/BoardOrdersView/OrdersList/OrderList.tsx b/common-front/lib/components/Orders/BoardOrdersView/OrdersList/OrderList.tsx deleted file mode 100644 index 0766e8f67..000000000 --- a/common-front/lib/components/Orders/BoardOrdersView/OrdersList/OrderList.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import styles from "./OrdersList.module.scss"; -import { - Form as FormType, - Order, - OrderDescription, - useSendOrder, -} from "../../../.."; -import { createFormFromOrder } from "../../useOrders"; -import { Form } from "./Form/Form"; -type Props = { - title: string; - orders: OrderDescription[]; -}; - -export const OrdersList = ({ title, orders }: Props) => { - const sendOrder = useSendOrder(); - - return ( - <div className={styles.orderList}> - <span className={styles.title}>{title}</span> - <div className={styles.orders}> - {orders.map((order) => ( - <Form - key={order.id} - initialForm={createFormFromOrder(order)} - onSubmit={(ev) => { - sendOrder(createOrder(order.id, ev)); - }} - /> - ))} - </div> - </div> - ); -}; - -export function createOrder(id: number, form: FormType): Order { - return { - id: id, - fields: Object.fromEntries( - form.fields.map((field) => { - if (field.type == "expandablePairs") { - return [field.id, field.value as any]; - } - - return [field.id, { value: field.value!, isEnabled: true }]; - }) - ), - }; -} diff --git a/common-front/lib/components/Orders/BoardOrdersView/OrdersList/OrdersList.module.scss b/common-front/lib/components/Orders/BoardOrdersView/OrdersList/OrdersList.module.scss deleted file mode 100644 index 7cb89b9b5..000000000 --- a/common-front/lib/components/Orders/BoardOrdersView/OrdersList/OrdersList.module.scss +++ /dev/null @@ -1,16 +0,0 @@ -.orderList { - display: flex; - flex-direction: column; - gap: 1rem; -} - -.title { - color: rgb(145, 145, 145); - font-weight: 600; -} - -.orders { - display: flex; - flex-direction: column; - gap: 1rem; -} diff --git a/common-front/lib/components/Orders/OrderContext.ts b/common-front/lib/components/Orders/OrderContext.ts new file mode 100644 index 000000000..fe91457a3 --- /dev/null +++ b/common-front/lib/components/Orders/OrderContext.ts @@ -0,0 +1,4 @@ +import { Order } from '../..'; +import { createContext } from 'react'; + +export const OrderContext = createContext<(order: Order) => void>(() => {}); diff --git a/common-front/lib/components/Orders/Orders.module.scss b/common-front/lib/components/Orders/Orders.module.scss index fb0df0651..f34f45b26 100644 --- a/common-front/lib/components/Orders/Orders.module.scss +++ b/common-front/lib/components/Orders/Orders.module.scss @@ -1,15 +1,37 @@ -.orders { - flex: 1 1 0; +.ordersWrapper { + width: 100%; + height: 100%; + min-height: 0; display: flex; flex-direction: column; - align-items: stretch; - gap: 1rem; overflow-y: auto; + gap: 0.5rem; +} + +.stateOrdersToggle { + top: 0px; + position: sticky; + display: flex; + flex-direction: column; + gap: 0.5rem; + z-index: 1; + background-color: white; + font-size: 0.8rem; + align-items: center; +} + +.stateOrdersToggleButton { + font-size: 0.8rem; } -.forms { +.boardOrderList { display: flex; flex-direction: column; gap: 0.5rem; - padding: 8px; +} + +.boardOrderList > :not(:last-child)::after { + content: ''; + border-bottom: 1px solid rgba(165, 131, 101, 0.368); + margin-bottom: 0.5rem; } diff --git a/common-front/lib/components/Orders/Orders.tsx b/common-front/lib/components/Orders/Orders.tsx index f56fa3c77..85d94d683 100644 --- a/common-front/lib/components/Orders/Orders.tsx +++ b/common-front/lib/components/Orders/Orders.tsx @@ -1,23 +1,48 @@ -import styles from "./Orders.module.scss"; -import { useOrders } from "./useOrders"; -import { BoardOrdersView } from "./BoardOrdersView/BoardOrdersView"; -import { BoardOrders, OrderDescription } from "../.."; - +import styles from './Orders.module.scss'; +import { BoardOrders, Button, useSendOrder } from '../..'; +import { OrderContext } from './OrderContext'; +import { BoardOrdersView } from './BoardOrders/BoardOrders'; +import { useState } from 'react'; type Props = { - orders: BoardOrders[]; + boards: BoardOrders[]; }; -export const Orders = ({ orders }: Props) => { +export const Orders = ({ boards }: Props) => { + const sendOrder = useSendOrder(); + const [alwaysShowStateOrders, setAlwaysShowStateOrders] = useState(false); + return ( - <div className={styles.orders}> - {orders.map((board) => ( - <BoardOrdersView - key={board.name} - board={board} - showName={true} - /> - ))} - </div> + <OrderContext.Provider value={sendOrder}> + <div className={styles.ordersWrapper}> + <div className={styles.stateOrdersToggle}> + Always show state orders:{' '} + {alwaysShowStateOrders ? 'true' : 'false'} + <Button + className={styles.stateOrdersToggleButton} + label="Toggle" + onClick={() => + setAlwaysShowStateOrders((prev) => !prev) + } + /> + </div> + <div className={styles.boardOrderList}> + {boards.map((board) => { + return ( + (board.orders.length > 0 || + board.stateOrders.length > 0) && ( + <BoardOrdersView + key={board.name} + boardOrders={board} + alwaysShowStateOrders={ + alwaysShowStateOrders + } + /> + ) + ); + })} + </div> + </div> + </OrderContext.Provider> ); }; diff --git a/common-front/lib/components/Orders/index.ts b/common-front/lib/components/Orders/index.ts deleted file mode 100644 index dab1f7c37..000000000 --- a/common-front/lib/components/Orders/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./Orders"; -export * from "./useOrders"; -export * from "./BoardOrdersView/BoardOrdersView"; diff --git a/common-front/lib/components/SplashScreen/SplashScreen.module.scss b/common-front/lib/components/SplashScreen/SplashScreen.module.scss new file mode 100644 index 000000000..2cfa25c51 --- /dev/null +++ b/common-front/lib/components/SplashScreen/SplashScreen.module.scss @@ -0,0 +1,13 @@ +.splash_screen { + display: flex; + flex-flow: column; + flex: 1 1 0; + justify-content: center; + align-items: center; + gap: 2rem; + + font-size: 6rem; + font-weight: 900; + + color: rgb(241, 105, 52); +} diff --git a/common-front/lib/components/SplashScreen/SplashScreen.tsx b/common-front/lib/components/SplashScreen/SplashScreen.tsx new file mode 100644 index 000000000..b8336a9d4 --- /dev/null +++ b/common-front/lib/components/SplashScreen/SplashScreen.tsx @@ -0,0 +1,25 @@ +import { ReactNode } from 'react'; +import styles from './SplashScreen.module.scss'; +import { animated, useSpring } from '@react-spring/web'; + +type Props = { + children?: ReactNode; +}; + +export const SplashScreen = ({ children }: Props) => { + const springs = useSpring({ + from: { transform: 'scale(0)' }, + to: { transform: 'scale(1)' }, + config: { + mass: 5, + }, + delay: 150, + loop: true, + }); + + return ( + <div className={styles.splash_screen}> + <animated.div style={{ ...springs }}>🐒{children}</animated.div> + </div> + ); +}; diff --git a/common-front/lib/components/index.ts b/common-front/lib/components/index.ts index 457f7e3e8..bbc608527 100644 --- a/common-front/lib/components/index.ts +++ b/common-front/lib/components/index.ts @@ -1,18 +1,21 @@ -export * from "./ColorfulChart/ColorfulChart"; -export * from "./LinesChart/LinesChart"; -export * from "./LabeledCamera/LabeledCamera"; -export * from "./Loader/Loader"; -export * from "./AnimatedFan/AnimatedFan"; -export * from "./LinesChart/types"; -export * from "./ButtonTag/ButtonTag"; -export * from "./ButtonTag/ButtonTag"; -export * from "./InputTag/InputTag"; -export * from "./ToggleInput/ToggleInput"; -export * from "./ToggleSwitch/ToggleSwitch"; -export * from "./PageWrapper/PageWrapper"; -export * from "./GaugeTag/GaugeTag"; -export * from "./FormComponents"; -export * from "./MessagesContainer"; -export * from "./Connections"; -export * from "./Logger"; -export * from "./Orders"; +export * from './ColorfulChart/ColorfulChart'; +export * from './LinesChart/LinesChart'; +export * from './LabeledCamera/LabeledCamera'; +export * from './Loader/Loader'; +export * from './AnimatedFan/AnimatedFan'; +export * from './LinesChart/types'; +export * from './ButtonTag/ButtonTag'; +export * from './ButtonTag/ButtonTag'; +export * from './InputTag/InputTag'; +export * from './ToggleInput/ToggleInput'; +export * from './ToggleSwitch/ToggleSwitch'; +export * from './PageWrapper/PageWrapper'; +export * from './GaugeTag/GaugeTag'; +export * from './FormComponents'; +export * from './MessagesContainer'; +export * from './Connections'; +export * from './Logger'; +export * from './Orders/Orders'; +export * from './Orders/useOrders'; +export * from './SplashScreen/SplashScreen'; +export * from './Caret/Caret'; diff --git a/common-front/lib/hooks/useListenKey.ts b/common-front/lib/hooks/useListenKey.ts index dd1cb8a09..cb63d7375 100644 --- a/common-front/lib/hooks/useListenKey.ts +++ b/common-front/lib/hooks/useListenKey.ts @@ -1,4 +1,4 @@ -import { useCallback, useEffect } from "react"; +import { useCallback, useEffect } from 'react'; export function useListenKey( key: string, @@ -17,11 +17,11 @@ export function useListenKey( useEffect(() => { if (listen) { - document.addEventListener("keydown", listener); + document.addEventListener('keydown', listener); } return () => { - document.removeEventListener("keydown", listener); + document.removeEventListener('keydown', listener); }; }, [listen, key, listener, callback]); } diff --git a/common-front/lib/hooks/useLoadBackend.ts b/common-front/lib/hooks/useLoadBackend.ts index 682a54521..1ed824d2a 100644 --- a/common-front/lib/hooks/useLoadBackend.ts +++ b/common-front/lib/hooks/useLoadBackend.ts @@ -1,27 +1,47 @@ -import { useEffect, useState } from "react"; -import { WsHandler, createWsHandler, fetchBack, useConfig, useConnectionsStore, useFetchBack, useMeasurementsStore, usePodDataStore } from ".." - -type loadBackendResult = { - state: "pending"; -} | { - state: "fulfilled"; - wsHandler: WsHandler; -} | { - state: "rejected"; - error: Error; -}; +import { useEffect, useState } from 'react'; +import { + WsHandler, + createWsHandler, + fetchBack, + useConfig, + useConnectionsStore, + useFetchBack, + useMeasurementsStore, + useOrdersStore, + usePodDataStore, +} from '..'; + +type loadBackendResult = + | { + state: 'pending'; + } + | { + state: 'fulfilled'; + wsHandler: WsHandler; + } + | { + state: 'rejected'; + error: Error; + }; type UseLoadBackend = (isProduction: boolean) => loadBackendResult; // Custom hook that initializes the Websocket connection, returning the WsHandler object // and fetches the podDataDescription from the server, initializing the PodData and Measurements in the store. -export const useLoadBackend : UseLoadBackend = (isProduction) => { +export const useLoadBackend: UseLoadBackend = (isProduction) => { const config = useConfig(); const initPodData = usePodDataStore((state) => state.initPodData); - const initMeasurements = useMeasurementsStore((state) => state.initMeasurements); - const setBackendConnection = useConnectionsStore((state) => state.setBackendConnection); - - const [result, setResult] = useState<loadBackendResult>({state: "pending"}); + const initMeasurements = useMeasurementsStore( + (state) => state.initMeasurements + ); + const setBackendConnection = useConnectionsStore( + (state) => state.setBackendConnection + ); + const setOrders = useOrdersStore((state) => state.setOrders); + + const [result, setResult] = useState<loadBackendResult>({ + state: 'pending', + }); const BACKEND_URL = isProduction ? `${config.prodServer.ip}:${config.prodServer.port}/${config.paths.websocket}` @@ -30,25 +50,40 @@ export const useLoadBackend : UseLoadBackend = (isProduction) => { const POD_DATA_DESCRIPTION_URL = isProduction ? `http://${config.prodServer.ip}:${config.prodServer.port}/${config.paths.podDataDescription}` : `http://${config.devServer.ip}:${config.devServer.port}/${config.paths.podDataDescription}`; + const ORDER_DESCRIPTION_URL = isProduction + ? `http://${config.prodServer.ip}:${config.prodServer.port}/${config.paths.orderDescription}` + : `http://${config.devServer.ip}:${config.devServer.port}/${config.paths.orderDescription}`; useEffect(() => { - Promise.all([ - createWsHandler( - BACKEND_URL, - true, - () => setBackendConnection(true), - () => setBackendConnection(false) - ), - fetch(POD_DATA_DESCRIPTION_URL).then((res) => res.json()).then( - (adapter) => { - initPodData(adapter); - initMeasurements(adapter); - } - ), - ]) - .then(result => setResult({state: "fulfilled", wsHandler: result[0]})) - .catch(error => setResult({state: "rejected", error})); + function requestBackend() { + Promise.all([ + createWsHandler( + BACKEND_URL, + true, + () => setBackendConnection(true), + () => setBackendConnection(false) + ), + fetch(POD_DATA_DESCRIPTION_URL) + .then((res) => res.json()) + .then((adapter) => { + initPodData(adapter); + initMeasurements(adapter); + }), + fetch(ORDER_DESCRIPTION_URL) + .then((res) => res.json()) + .then((adapter) => setOrders(adapter)), + ]) + .then((result) => + setResult({ state: 'fulfilled', wsHandler: result[0] }) + ) + .catch((error) => { + setResult({ state: 'rejected', error }); + setTimeout(requestBackend, 1000); + }); + } + + requestBackend(); }, []); return result; -} +}; diff --git a/common-front/lib/index.ts b/common-front/lib/index.ts index 294ae0273..dad232c17 100644 --- a/common-front/lib/index.ts +++ b/common-front/lib/index.ts @@ -1,16 +1,17 @@ -export * from "./adapters"; -export * from "./broker"; -export * from "./wsHandler"; -export * from "./models"; -export * from "./store"; -export * from "./BackendTypes"; -export * from "./components"; -export * from "./hooks"; -export * from "./services"; -export * from "./config/config"; -export * from "./styles"; -export * from "./form"; -export * from "./config"; -export * from "./Suspense"; -export * from "./math"; -export * from "./selectors"; +export * from './adapters'; +export * from './broker'; +export * from './wsHandler'; +export * from './models'; +export * from './store'; +export * from './BackendTypes'; +export * from './components'; +export * from './hooks'; +export * from './services'; +export * from './config/config'; +export * from './styles'; +export * from './form'; +export * from './config'; +export * from './Suspense'; +export * from './math'; +export * from './color'; +export * from './selectors'; diff --git a/common-front/lib/selectors/BCU.ts b/common-front/lib/selectors/BCU.ts new file mode 100644 index 000000000..f60afacee --- /dev/null +++ b/common-front/lib/selectors/BCU.ts @@ -0,0 +1,8 @@ +export const BcuMeasurements = { + bpu1CurrentU: 'BCU/current_u_bpu_1_sector_1', + bpu1CurrentV: 'BCU/current_v_bpu_1_sector_1', + bpu1CurrentW: 'BCU/current_w_bpu_1_sector_1', + bpu2CurrentU: 'BCU/current_u_bpu_2_sector_1', + bpu2CurrentV: 'BCU/current_v_bpu_2_sector_1', + bpu2CurrentW: 'BCU/current_w_bpu_2_sector_1', +}; diff --git a/common-front/lib/selectors/BMSL.ts b/common-front/lib/selectors/BMSL.ts index 2945b4811..0b4069f1a 100644 --- a/common-front/lib/selectors/BMSL.ts +++ b/common-front/lib/selectors/BMSL.ts @@ -1,26 +1,12 @@ - -export enum BmslMeasurements { - avCurrent = "BMSL/AV_current", - lowCell1 = "BMSL/low_cell1", - lowCell2 = "BMSL/low_cell2", - lowCell3 = "BMSL/low_cell3", - lowCell4 = "BMSL/low_cell4", - lowCell5 = "BMSL/low_cell5", - lowCell6 = "BMSL/low_cell6", - lowSOC1 = "BMSL/low_SOC1", - lowIsBalancing1 = "BMSL/low_is_balancing1", - lowMaximumCell = "BMSL/low_maximum_cell", - lowMinimumCell = "BMSL/low_minimum_cell", - lowBatteryTemperature1 = "BMSL/low_battery_temperature_1", - lowBatteryTemperature2 = "BMSL/low_battery_temperature_2", - totalVoltageLow = "BMSL/total_voltage_low", - inputChargingCurrent = "BMSL/input_charging_current", - outputChargingCurrent = "BMSL/output_charging_current", - inputChargingVoltage = "BMSL/input_charging_voltage", - outputChargingVoltage = "BMSL/output_charging_voltage", - pwmFrequency = "BMSL/pwm_frequency", - conditionsReady = "BMSL/conditions_ready", - conditionsWantToCharge = "BMSL/conditions_want_to_charge", - conditionsCharging = "BMSL/conditions_charging", - conditionsFault = "BMSL/conditions_fault" -} \ No newline at end of file +export const BmslMeasurements = { + cell1: 'BMSL/test_cell_1', + cell2: 'BMSL/test_cell_2', + cell3: 'BMSL/test_cell_3', + cell4: 'BMSL/test_cell_4', + cell5: 'BMSL/test_cell_5', + cell6: 'BMSL/test_cell_6', + temp1: 'BMSL/test_temp_1', + temp2: 'BMSL/test_temp_2', + totalVoltage: 'BMSL/test_total_voltage', + dischargeCurrent: 'BMSL/test_distcharge_current', +}; diff --git a/common-front/lib/selectors/LCU.ts b/common-front/lib/selectors/LCU.ts index 28b1ae067..49fa60b96 100644 --- a/common-front/lib/selectors/LCU.ts +++ b/common-front/lib/selectors/LCU.ts @@ -1,243 +1,42 @@ -import { - EnumMeasurement, - Measurements, - NumericMeasurement, - useMeasurementsStore -} from ".."; - -export type LcuMeasurements = { - general_state: EnumMeasurement; - specific_state: EnumMeasurement; - slave_general_state: EnumMeasurement; - slave_specific_state: EnumMeasurement; - - airgap_1: NumericMeasurement; - airgap_2: NumericMeasurement; - airgap_3: NumericMeasurement; - airgap_4: NumericMeasurement; - slave_airgap_5: NumericMeasurement; - slave_airgap_6: NumericMeasurement; - slave_airgap_7: NumericMeasurement; - slave_airgap_8: NumericMeasurement; - - current_coil_1: NumericMeasurement; - current_coil_2: NumericMeasurement; - current_coil_3: NumericMeasurement; - current_coil_4: NumericMeasurement; - slave_current_coil_5: NumericMeasurement; - slave_current_coil_6: NumericMeasurement; - slave_current_coil_7: NumericMeasurement; - slave_current_coil_8: NumericMeasurement; - - temperature_hems_1: NumericMeasurement; - temperature_hems_2: NumericMeasurement; - temperature_hems_3: NumericMeasurement; - temperature_hems_4: NumericMeasurement; - - temperature_ems_1: NumericMeasurement; - temperature_ems_2: NumericMeasurement; - temperature_ems_3: NumericMeasurement; - temperature_ems_4: NumericMeasurement; - - temperature_lpu_1: NumericMeasurement; - temperature_lpu_2: NumericMeasurement; - temperature_lpu_3: NumericMeasurement; - temperature_lpu_4: NumericMeasurement; - slave_temperature_lpu_5: NumericMeasurement; - slave_temperature_lpu_6: NumericMeasurement; - slave_temperature_lpu_7: NumericMeasurement; - slave_temperature_lpu_8: NumericMeasurement; - - battery_voltage_1: NumericMeasurement; - battery_voltage_2: NumericMeasurement; - slave_battery_voltage_3: NumericMeasurement; - slave_battery_voltage_4: NumericMeasurement; - - reference_current_1: NumericMeasurement; - reference_current_2: NumericMeasurement; - reference_current_3: NumericMeasurement; - reference_current_4: NumericMeasurement; - slave_reference_current_5: NumericMeasurement; - slave_reference_current_6: NumericMeasurement; - slave_reference_current_7: NumericMeasurement; - slave_reference_current_8: NumericMeasurement; - - rot_x: NumericMeasurement; - rot_y: NumericMeasurement; - rot_z: NumericMeasurement; - - control_state: EnumMeasurement; +export const LcuMeasurements = { + coilCurrentHEMS1: 'LCU/lcu_coil_current_1', + coilCurrentHEMS2: 'LCU/lcu_coil_current_2', + coilCurrentHEMS3: 'LCU/lcu_coil_current_3', + coilCurrentHEMS4: 'LCU/lcu_coil_current_4', + coilCurrentEMS1: 'LCU/lcu_coil_current_5', + coilCurrentEMS2: 'LCU/lcu_coil_current_6', + coilCurrentEMS3: 'LCU/lcu_coil_current_7', + coilCurrentEMS4: 'LCU/lcu_coil_current_8', + coilCurrentEMS5: 'LCU/lcu_coil_current_9', + coilCurrentEMS6: 'LCU/lcu_coil_current_10', + coilTemperatureHEMS1: 'LCU/lcu_coil_temp_1', + coilTemperatureHEMS2: 'LCU/lcu_coil_temp_2', + coilTemperatureHEMS3: 'LCU/lcu_coil_temp_3', + coilTemperatureHEMS4: 'LCU/lcu_coil_temp_4', + coilTemperatureEMS1: 'LCU/lcu_coil_temp_5', + coilTemperatureEMS2: 'LCU/lcu_coil_temp_6', + coilTemperatureEMS3: 'LCU/lcu_coil_temp_7', + coilTemperatureEMS4: 'LCU/lcu_coil_temp_8', + coilTemperatureEMS5: 'LCU/lcu_coil_temp_9', + coilTemperatureEMS6: 'LCU/lcu_coil_temp_10', + verticalAirgap1: 'LCU/lcu_airgap_1', + verticalAirgap2: 'LCU/lcu_airgap_2', + verticalAirgap3: 'LCU/lcu_airgap_3', + verticalAirgap4: 'LCU/lcu_airgap_4', + horizontalAirgap1: 'LCU/lcu_airgap_5', + horizontalAirgap2: 'LCU/lcu_airgap_6', + horizontalAirgap3: 'LCU/lcu_airgap_7', + horizontalAirgap4: 'LCU/lcu_airgap_8', + positionY: 'LCU/pos_y', + positionZ: 'LCU/pos_z', + rotationPitch: 'LCU/pos_rot_y', + rotationRoll: 'LCU/pos_rot_x', + rotationYaw: 'LCU/pos_rot_z', }; -export function selectLcuMeasurements( - measurements: Measurements -): LcuMeasurements { - - const getMeasurementFallback = useMeasurementsStore(state => state.getMeasurementFallback); - - return { - general_state: getMeasurementFallback( - "LCU_MASTER/lcu_master_general_state" - ), - specific_state: getMeasurementFallback( - "LCU_MASTER/lcu_master_specific_state" - ), - slave_general_state: getMeasurementFallback( - "LCU_MASTER/lcu_master_slave_general_state" - ), - slave_specific_state: getMeasurementFallback( - "LCU_MASTER/lcu_master_slave_specific_state" - ), - - airgap_1: getMeasurementFallback( - "LCU_MASTER/lcu_master_airgap_1" - ), - airgap_2: getMeasurementFallback( - "LCU_MASTER/lcu_master_slave_airgap_2" - ), - airgap_3: getMeasurementFallback( - "LCU_MASTER/lcu_master_airgap_3" - ), - airgap_4: getMeasurementFallback( - "LCU_MASTER/lcu_master_slave_airgap_4" - ), - slave_airgap_5: getMeasurementFallback( - "LCU_MASTER/lcu_master_airgap_5" - ), - slave_airgap_6: getMeasurementFallback( - "LCU_MASTER/lcu_master_slave_airgap_6" - ), - slave_airgap_7: getMeasurementFallback( - "LCU_MASTER/lcu_master_airgap_7" - ), - slave_airgap_8: getMeasurementFallback( - "LCU_MASTER/lcu_master_slave_airgap_8" - ), - - current_coil_1: getMeasurementFallback( - "LCU_MASTER/lcu_master_current_coil_hems_1" - ), - current_coil_2: getMeasurementFallback( - "LCU_MASTER/lcu_master_current_coil_hems_3" - ), - current_coil_3: getMeasurementFallback( - "LCU_MASTER/lcu_master_current_coil_ems_1" - ), - current_coil_4: getMeasurementFallback( - "LCU_MASTER/lcu_master_current_coil_ems_3" - ), - slave_current_coil_5: getMeasurementFallback( - "LCU_MASTER/lcu_master_slave_current_coil_hems_2" - ), - slave_current_coil_6: getMeasurementFallback( - "LCU_MASTER/lcu_master_slave_current_coil_hems_4" - ), - slave_current_coil_7: getMeasurementFallback( - "LCU_MASTER/lcu_master_slave_current_coil_ems_2" - ), - slave_current_coil_8: getMeasurementFallback( - "LCU_MASTER/lcu_master_slave_current_coil_ems_4" - ), - - temperature_hems_1: getMeasurementFallback( - "LCU_MASTER/lcu_master_temperature_hems_1" - ), - temperature_hems_2: getMeasurementFallback( - "LCU_MASTER/lcu_master_temperature_hems_2" - ), - temperature_hems_3: getMeasurementFallback( - "LCU_MASTER/lcu_master_temperature_hems_3" - ), - temperature_hems_4: getMeasurementFallback( - "LCU_MASTER/lcu_master_temperature_hems_4" - ), - temperature_ems_1: getMeasurementFallback( - "LCU_MASTER/lcu_master_temperature_ems_1" - ), - temperature_ems_2: getMeasurementFallback( - "LCU_MASTER/lcu_master_temperature_ems_2" - ), - temperature_ems_3: getMeasurementFallback( - "LCU_MASTER/lcu_master_temperature_ems_3" - ), - temperature_ems_4: getMeasurementFallback( - "LCU_MASTER/lcu_master_temperature_ems_4" - ), - - temperature_lpu_1: getMeasurementFallback( - "LCU_MASTER/lcu_master_temperature_lpu_1" - ), - temperature_lpu_2: getMeasurementFallback( - "LCU_MASTER/lcu_master_temperature_lpu_2" - ), - temperature_lpu_3: getMeasurementFallback( - "LCU_MASTER/lcu_master_temperature_lpu_3" - ), - temperature_lpu_4: getMeasurementFallback( - "LCU_MASTER/lcu_master_temperature_lpu_4" - ), - slave_temperature_lpu_5: getMeasurementFallback( - "LCU_MASTER/lcu_master_slave_temperature_lpu_5" - ), - slave_temperature_lpu_6: getMeasurementFallback( - "LCU_MASTER/lcu_master_slave_temperature_lpu_6" - ), - slave_temperature_lpu_7: getMeasurementFallback( - "LCU_MASTER/lcu_master_slave_temperature_lpu_7" - ), - slave_temperature_lpu_8: getMeasurementFallback( - "LCU_MASTER/lcu_master_slave_temperature_lpu_8" - ), - - battery_voltage_1: getMeasurementFallback( - "LCU_MASTER/lcu_master_battery_voltage_1" - ), - battery_voltage_2: getMeasurementFallback( - "LCU_MASTER/lcu_master_battery_voltage_2" - ), - slave_battery_voltage_3: getMeasurementFallback( - "LCU_MASTER/lcu_master_slave_battery_voltage_3" - ), - slave_battery_voltage_4: getMeasurementFallback( - "LCU_MASTER/lcu_master_slave_battery_voltage_4" - ), - - reference_current_1: getMeasurementFallback( - "LCU_MASTER/lcu_master_reference_current_hems_1" - ), - reference_current_2: getMeasurementFallback( - "LCU_MASTER/lcu_master_reference_current_hems_2" - ), - reference_current_3: getMeasurementFallback( - "LCU_MASTER/lcu_master_reference_current_hems_3" - ), - reference_current_4: getMeasurementFallback( - "LCU_MASTER/lcu_master_reference_current_hems_4" - ), - slave_reference_current_5: getMeasurementFallback( - "LCU_MASTER/lcu_master_reference_current_ems_1" - ), - slave_reference_current_6: getMeasurementFallback( - "LCU_MASTER/lcu_master_reference_current_ems_2" - ), - slave_reference_current_7: getMeasurementFallback( - "LCU_MASTER/lcu_master_reference_current_ems_3" - ), - slave_reference_current_8: getMeasurementFallback( - "LCU_MASTER/lcu_master_reference_current_ems_4" - ), - - rot_x: getMeasurementFallback( - "LCU_MASTER/lcu_master_rot_x" - ), - rot_y: getMeasurementFallback( - "LCU_MASTER/lcu_master_rot_y" - ), - rot_z: getMeasurementFallback( - "LCU_MASTER/lcu_master_rot_z" - ), - - control_state: - measurements["LCU_MASTER/lcu_master_control_state"], - } as LcuMeasurements; -} +export const LcuOrders = { + startLevitationControl: 355, + startVerticalLevitation: 356, + startHorizontalLevitation: 360, + stopLevitation: 357, +}; diff --git a/common-front/lib/selectors/OBCCU.ts b/common-front/lib/selectors/OBCCU.ts index c702e948a..d518358a6 100644 --- a/common-front/lib/selectors/OBCCU.ts +++ b/common-front/lib/selectors/OBCCU.ts @@ -1,81 +1,67 @@ export enum ObccuMeasurements { - generalState = "OBCCU/general_state", - maximumCell1 = "OBCCU/maximum_cell_1", - maximumCell2 = "OBCCU/maximum_cell_2", - maximumCell3 = "OBCCU/maximum_cell_3", - maximumCell4 = "OBCCU/maximum_cell_4", - maximumCell5 = "OBCCU/maximum_cell_5", - maximumCell6 = "OBCCU/maximum_cell_6", - maximumCell7 = "OBCCU/maximum_cell_7", - maximumCell8 = "OBCCU/maximum_cell_8", - maximumCell9 = "OBCCU/maximum_cell_9", - maximumCell10 = "OBCCU/maximum_cell_10", - minimumCell1 = "OBCCU/minimum_cell_1", - minimumCell2 = "OBCCU/minimum_cell_2", - minimumCell3 = "OBCCU/minimum_cell_3", - minimumCell4 = "OBCCU/minimum_cell_4", - minimumCell5 = "OBCCU/minimum_cell_5", - minimumCell6 = "OBCCU/minimum_cell_6", - minimumCell7 = "OBCCU/minimum_cell_7", - minimumCell8 = "OBCCU/minimum_cell_8", - minimumCell9 = "OBCCU/minimum_cell_9", - minimumCell10 = "OBCCU/minimum_cell_10", - SOC1 = "OBCCU/SOC1", - SOC2 = "OBCCU/SOC2", - SOC3 = "OBCCU/SOC3", - SOC4 = "OBCCU/SOC4", - SOC5 = "OBCCU/SOC5", - SOC6 = "OBCCU/SOC6", - SOC7 = "OBCCU/SOC7", - SOC8 = "OBCCU/SOC8", - SOC9 = "OBCCU/SOC9", - SOC10 = "OBCCU/SOC10", - isBalancing1 = "OBCCU/is_balancing1", - isBalancing2 = "OBCCU/is_balancing2", - isBalancing3 = "OBCCU/is_balancing3", - isBalancing4 = "OBCCU/is_balancing4", - isBalancing5 = "OBCCU/is_balancing5", - isBalancing6 = "OBCCU/is_balancing6", - isBalancing7 = "OBCCU/is_balancing7", - isBalancing8 = "OBCCU/is_balancing8", - isBalancing9 = "OBCCU/is_balancing9", - isBalancing10 = "OBCCU/is_balancing10", - dclvTemperature = "OBCCU/dclv_temperature", - inverterTemperature = "OBCCU/inverter_temperature", - transformerTemperature = "OBCCU/transformer_temperature", - rectifierTemperature = "OBCCU/rectifier_temperature", - resonantTankTemperature = "OBCCU/resonant_tank_temperature", - battery_temperature_1 = "OBCCU/battery_temperature_1", - battery_temperature_2 = "OBCCU/battery_temperature_2", - battery_temperature_3 = "OBCCU/battery_temperature_3", - battery_temperature_4 = "OBCCU/battery_temperature_4", - battery_temperature_5 = "OBCCU/battery_temperature_5", - battery_temperature_6 = "OBCCU/battery_temperature_6", - battery_temperature_7 = "OBCCU/battery_temperature_7", - battery_temperature_8 = "OBCCU/battery_temperature_8", - battery_temperature_9 = "OBCCU/battery_temperature_9", - battery_temperature_10 = "OBCCU/battery_temperature_10", - "2BatteryTemperature1" = "OBCCU/2battery_temperature_1", - "2BatteryTemperature2" = "OBCCU/2battery_temperature_2", - "2BatteryTemperature3" = "OBCCU/2battery_temperature_3", - "2BatteryTemperature4" = "OBCCU/2battery_temperature_4", - "2BatteryTemperature5" = "OBCCU/2battery_temperature_5", - "2BatteryTemperature6" = "OBCCU/2battery_temperature_6", - "2BatteryTemperature7" = "OBCCU/2battery_temperature_7", - "2BatteryTemperature8" = "OBCCU/2battery_temperature_8", - "2BatteryTemperature9" = "OBCCU/2battery_temperature_9", - "2BatteryTemperature10" = "OBCCU/2battery_temperature_10", - imd = "OBCCU/imd", - totalVoltage1 = "OBCCU/total_voltage1", - totalVoltage2 = "OBCCU/total_voltage2", - totalVoltage3 = "OBCCU/total_voltage3", - totalVoltage4 = "OBCCU/total_voltage4", - totalVoltage5 = "OBCCU/total_voltage5", - totalVoltage6 = "OBCCU/total_voltage6", - totalVoltage7 = "OBCCU/total_voltage7", - totalVoltage8 = "OBCCU/total_voltage8", - totalVoltage9 = "OBCCU/total_voltage9", - totalVoltage10 = "OBCCU/total_voltage10", - totalVoltageHigh = "OBCCU/total_voltage_high", - drift = "OBCCU/drift", -}; \ No newline at end of file + stateOfCharge1 = 'OBCCU/SOC1', + stateOfCharge2 = 'OBCCU/SOC2', + stateOfCharge3 = 'OBCCU/SOC3', + stateOfCharge4 = 'OBCCU/SOC4', + stateOfCharge5 = 'OBCCU/SOC5', + stateOfCharge6 = 'OBCCU/SOC6', + stateOfCharge7 = 'OBCCU/SOC7', + stateOfCharge8 = 'OBCCU/SOC8', + stateOfCharge9 = 'OBCCU/SOC9', + stateOfCharge10 = 'OBCCU/SOC10', + batteryTemperature1 = 'OBCCU/battery_temperature_1', + batteryTemperature2 = 'OBCCU/battery_temperature_2', + batteryTemperature3 = 'OBCCU/battery_temperature_3', + batteryTemperature4 = 'OBCCU/battery_temperature_4', + batteryTemperature5 = 'OBCCU/battery_temperature_5', + batteryTemperature6 = 'OBCCU/battery_temperature_6', + batteryTemperature7 = 'OBCCU/battery_temperature_7', + batteryTemperature8 = 'OBCCU/battery_temperature_8', + batteryTemperature9 = 'OBCCU/battery_temperature_9', + batteryTemperature10 = 'OBCCU/battery_temperature_10', + maximumCell1 = 'OBCCU/maximum_cell_1', + maximumCell2 = 'OBCCU/maximum_cell_2', + maximumCell3 = 'OBCCU/maximum_cell_3', + maximumCell4 = 'OBCCU/maximum_cell_4', + maximumCell5 = 'OBCCU/maximum_cell_5', + maximumCell6 = 'OBCCU/maximum_cell_6', + maximumCell7 = 'OBCCU/maximum_cell_7', + maximumCell8 = 'OBCCU/maximum_cell_8', + maximumCell9 = 'OBCCU/maximum_cell_9', + maximumCell10 = 'OBCCU/maximum_cell_10', + minimumCell1 = 'OBCCU/minimum_cell_1', + minimumCell2 = 'OBCCU/minimum_cell_2', + minimumCell3 = 'OBCCU/minimum_cell_3', + minimumCell4 = 'OBCCU/minimum_cell_4', + minimumCell5 = 'OBCCU/minimum_cell_5', + minimumCell6 = 'OBCCU/minimum_cell_6', + minimumCell7 = 'OBCCU/minimum_cell_7', + minimumCell8 = 'OBCCU/minimum_cell_8', + minimumCell9 = 'OBCCU/minimum_cell_9', + minimumCell10 = 'OBCCU/minimum_cell_10', + totalVoltage1 = 'OBCCU/total_voltage1', + totalVoltage2 = 'OBCCU/total_voltage2', + totalVoltage3 = 'OBCCU/total_voltage3', + totalVoltage4 = 'OBCCU/total_voltage4', + totalVoltage5 = 'OBCCU/total_voltage5', + totalVoltage6 = 'OBCCU/total_voltage6', + totalVoltage7 = 'OBCCU/total_voltage7', + totalVoltage8 = 'OBCCU/total_voltage8', + totalVoltage9 = 'OBCCU/total_voltage9', + totalVoltage10 = 'OBCCU/total_voltage10', + isBalancing1 = 'OBCCU/is_balancing1', + isBalancing2 = 'OBCCU/is_balancing2', + isBalancing3 = 'OBCCU/is_balancing3', + isBalancing4 = 'OBCCU/is_balancing4', + isBalancing5 = 'OBCCU/is_balancing5', + isBalancing6 = 'OBCCU/is_balancing6', + isBalancing7 = 'OBCCU/is_balancing7', + isBalancing8 = 'OBCCU/is_balancing8', + isBalancing9 = 'OBCCU/is_balancing9', + isBalancing10 = 'OBCCU/is_balancing10', + totalVoltageHigh = 'OBCCU/total_voltage_high', + dischargeCurrent = 'OBCCU/discharge_current_obccu', + imdState = 'OBCCU/STATUS', + contactorsState = 'OBCCU/contactors_state', + generalState = 'OBCCU/Board_state', +} diff --git a/common-front/lib/selectors/PCU.ts b/common-front/lib/selectors/PCU.ts index 21b5f8d3c..8f4e95ee1 100644 --- a/common-front/lib/selectors/PCU.ts +++ b/common-front/lib/selectors/PCU.ts @@ -1,96 +1,19 @@ -import { Measurements, NumericMeasurement, useMeasurementsStore } from ".."; - -export type PcuMeasurements = { - max_ppu_a_temperature: NumericMeasurement; - max_motor_a_temperature: NumericMeasurement; - - motor_a_current_u: NumericMeasurement; - motor_a_current_v: NumericMeasurement; - motor_a_current_w: NumericMeasurement; - - motor_b_current_u: NumericMeasurement; - motor_b_current_v: NumericMeasurement; - motor_b_current_w: NumericMeasurement; - - ppu_a_battery_voltage: NumericMeasurement; - ppu_a_battery_current: NumericMeasurement; - - accel_x: NumericMeasurement; - accel_y: NumericMeasurement; - accel_z: NumericMeasurement; - - velocity: NumericMeasurement; - accel: NumericMeasurement; - - duty_u: NumericMeasurement; - duty_v: NumericMeasurement; - duty_w: NumericMeasurement; - - peak_current: NumericMeasurement; +import { Measurements, NumericMeasurement, useMeasurementsStore } from '..'; + +export const PcuMeasurements = { + motorAPeakCurrent: 'PCU/peak_current', + motorACurrentU: 'PCU/motor_a_current_u', + motorACurrentV: 'PCU/motor_a_current_v', + motorACurrentW: 'PCU/motor_a_current_w', + motorATemp: 'PCU/max_motor_a_temperature', + motorBPeakCurrent: 'PCU/peak_current', + motorBCurrentU: 'PCU/motor_b_current_u', + motorBCurrentV: 'PCU/motor_b_current_v', + motorBCurrentW: 'PCU/motor_b_current_w', + motorBTemp: 'PCU/max_motor_a_temperature', + frequency: 'PCU/target_frequency', + generalState: 'PCU/general_state', + specificState: 'PCU/operating_state', }; -export function selectPcuMeasurements( - measurements: Measurements -): PcuMeasurements { - - const getMeasurementFallback = useMeasurementsStore(state => state.getMeasurementFallback); - - return { - max_ppu_a_temperature: getMeasurementFallback( - - "PCU/max_ppu_a_temperature" - ), - max_motor_a_temperature: getMeasurementFallback( - - "PCU/max_motor_a_temperature" - ), - - motor_a_current_u: getMeasurementFallback( - - "PCU/motor_a_current_u" - ), - motor_a_current_v: getMeasurementFallback( - - "PCU/motor_a_current_v" - ), - motor_a_current_w: getMeasurementFallback( - - "PCU/motor_a_current_w" - ), - - motor_b_current_u: getMeasurementFallback( - - "PCU/motor_b_current_u" - ), - motor_b_current_v: getMeasurementFallback( - - "PCU/motor_b_current_v" - ), - motor_b_current_w: getMeasurementFallback( - - "PCU/motor_b_current_w" - ), - - ppu_a_battery_voltage: getMeasurementFallback( - - "PCU/ppu_a_battery_voltage" - ), - ppu_a_battery_current: getMeasurementFallback( - - "PCU/ppu_a_battery_current" - ), - - accel_x: getMeasurementFallback( "PCU/accel_x"), - accel_y: getMeasurementFallback( "PCU/accel_y"), - accel_z: getMeasurementFallback( "PCU/accel_z"), - - velocity: getMeasurementFallback( "PCU/velocity"), - accel: getMeasurementFallback( "PCU/accel"), - - duty_u: getMeasurementFallback( "PCU/duty_u"), - duty_v: getMeasurementFallback( "PCU/duty_v"), - duty_w: getMeasurementFallback( "PCU/duty_w"), - - peak_current: getMeasurementFallback( "PCU/peak_current"), - } as PcuMeasurements; -} +export const PcuOrders = {}; diff --git a/common-front/lib/selectors/VCU.ts b/common-front/lib/selectors/VCU.ts index fe598ae84..9d0d25688 100644 --- a/common-front/lib/selectors/VCU.ts +++ b/common-front/lib/selectors/VCU.ts @@ -1,19 +1,19 @@ - export enum VcuMeasurements { - generalState = "VCU/general_state", - specificState = "VCU/specific_state", - voltageState = "VCU/voltage_state", - referencePressure = "VCU/reference_pressure", - actualPressure = "VCU/actual_pressure", - valveState = "VCU/valve_state", - reed1 = "VCU/reed1", - reed2 = "VCU/reed2", - reed3 = "VCU/reed3", - reed4 = "VCU/reed4", - bottleTemp1 = "VCU/bottle_temp_1", - bottleTemp2 = "VCU/bottle_temp_2", - highPressure = "VCU/high_pressure", - position = "VCU/position", - speed = "VCU/speed", - acceleration = "VCU/acceleration", -}; + reed1 = 'VCU/reed1', + reed2 = 'VCU/reed2', + reed3 = 'VCU/reed3', + reed4 = 'VCU/reed4', + highPressure = 'VCU/high_pressure', + lowPressure1 = 'VCU/low_pressure_1', + lowPressure2 = 'VCU/low_pressure_2', + valveState = 'VCU/valve_state', + referencePressure = 'VCU/reference_pressure', + position = 'VCU/position', + speed = 'VCU/speed', + acceleration = 'VCU/acceleration', + pcuConnection = 'VCU/PCU_connection', + obccuConnection = 'VCU/OBCCU_connection', + lcuConnection = 'VCU/LCU_connection', + generalState = 'VCU/general_state', + specificState = 'VCU/specific_state', +} diff --git a/common-front/lib/selectors/index.ts b/common-front/lib/selectors/index.ts index f4f4965fd..54bd9e633 100644 --- a/common-front/lib/selectors/index.ts +++ b/common-front/lib/selectors/index.ts @@ -1,6 +1,7 @@ -export * from "./BMSL"; -export * from "./LCU"; -export * from "./OBCCU"; -export * from "./PCU"; -export * from "./VCU"; -export * from "./TCU"; +export * from './BMSL'; +export * from './LCU'; +export * from './OBCCU'; +export * from './PCU'; +export * from './VCU'; +export * from './TCU'; +export * from './BCU'; diff --git a/common-front/lib/store/measurementsStore.ts b/common-front/lib/store/measurementsStore.ts index 896d24bb1..9f766901d 100644 --- a/common-front/lib/store/measurementsStore.ts +++ b/common-front/lib/store/measurementsStore.ts @@ -1,4 +1,3 @@ - import { Measurement, NumericMeasurement, @@ -14,7 +13,11 @@ import { } from '../adapters'; import { create } from 'zustand'; import { isNumericType } from '../BackendTypes'; -import { BooleanMeasurement, EnumMeasurement, NumericValue } from '../models/PodData/Measurement'; +import { + BooleanMeasurement, + EnumMeasurement, + NumericValue, +} from '../models/PodData/Measurement'; export type Measurements = Record<string, Measurement>; export type MeasurementId = string; @@ -29,6 +32,7 @@ export type NumericMeasurementInfo = { readonly id: MeasurementId; readonly name: MeasurementName; readonly range: [number | null, number | null]; + readonly warningRange: [number | null, number | null]; readonly color: MeasurementColor; readonly units: MeasurementUnits; readonly getUpdate: UpdateFunctionNumeric; @@ -85,7 +89,6 @@ export const useMeasurementsStore = create<MeasurementsStore>((set, get) => ({ * @param {Record<string, PacketUpdate>} measurements */ updateMeasurements: (measurements: Record<string, PacketUpdate>) => { - const measurementsDraft = get().measurements; for (const update of Object.values(measurements)) { @@ -165,7 +168,7 @@ export const useMeasurementsStore = create<MeasurementsStore>((set, get) => ({ return get().measurements[id]; }, - getBooleanMeasurementInfo: (id: string) : BooleanMeasurementInfo => { + getBooleanMeasurementInfo: (id: string): BooleanMeasurementInfo => { const meas = get().measurements[id] as BooleanMeasurement; return { id: meas.id, @@ -174,30 +177,31 @@ export const useMeasurementsStore = create<MeasurementsStore>((set, get) => ({ const meas = get().measurements[id] as BooleanMeasurement; if (meas == undefined) return false; return meas.value; - } + }, }; }, - getEnumMeasurementInfo: (id: string) : EnumMeasurementInfo => { + getEnumMeasurementInfo: (id: string): EnumMeasurementInfo => { const meas = get().measurements[id] as EnumMeasurement; return { id: meas.id, name: meas.name, getUpdate: () => { const meas = get().measurements[id] as EnumMeasurement; - if (meas == undefined) return "Default"; + if (meas == undefined) return 'Default'; return meas.value; - } + }, }; }, - getNumericMeasurementInfo: (id: string) : NumericMeasurementInfo => { + getNumericMeasurementInfo: (id: string): NumericMeasurementInfo => { const meas = get().measurements[id] as NumericMeasurement; return { id: meas.id, name: meas.name, units: meas.units, range: meas.safeRange, + warningRange: meas.warningRange, getUpdate: () => { const meas = get().measurements[id] as NumericMeasurement; if (meas == undefined) return 0; diff --git a/common-front/lib/styles/styles.scss b/common-front/lib/styles/styles.scss index 52e59a787..8c010e9d4 100644 --- a/common-front/lib/styles/styles.scss +++ b/common-front/lib/styles/styles.scss @@ -1,5 +1,128 @@ -@use "./colors.scss"; -@use "./fonts.scss"; +@use 'sass:color'; + +@use './colors.scss'; +@use './fonts.scss'; + +// COLORS +$background-color: #dce3eb; +$title-color: #45677d; +$normal-text-color: #373c43; +$alternate-text-color: #505868; +$orange: #ee7623; +$blue: #317ae7; +$base-color: hsl(212, 27%, 55%); + +$dark-normal-text-color: #bec6d2; + +@function getColor($name, $lightness) { + @return var(--color-#{$name}-#{$lightness}); +} + +// FONTS +$sans-font: Inter; +$code-font: Consolas, Cascadia Code, Cascadia Mono, Monospace; +$alternate-code-font: Consolas, Monospace; + +// FONT-SIZE +$title-font-size: 1.9rem; +$normal-font-size: 1rem; +$small-font-size: x-small; + +// FONT-WEIGHT +$bold-font-weight: 700; +$normal-font-weight: 400; + +// PADDING +$large-padding: 2rem; +$normal-padding: 1.2rem; + +// BORDER-RADIUS +$large-border-radius: 1rem; +$normal-border-radius: 0.2rem; + +// BORDER-WIDTH +$normal-border-width: 1px; + +// TRANSITIONS +$normal-transition-time: 0.08s; +$opacity-transition: opacity $normal-transition-time linear; +$background-color-transition: background-color $normal-transition-time linear; + +// MIXINS +@mixin code-text { + font-family: $code-font; + font-size: $normal-font-size; + color: $normal-text-color; +} + +@mixin alternate-code-text { + font-family: $alternate-code-font; + font-size: $normal-font-size; + color: $alternate-text-color; +} + +@mixin title-text { + font-family: $sans-font; + font-size: $title-font-size; + color: $title-color; + font-weight: $bold-font-weight; +} + +@mixin normal-text { + font-family: $sans-font; + font-size: $normal-font-size; + color: $alternate-text-color; + font-weight: $normal-font-weight; +} + +@mixin subtitle-text { + font-family: $code-font; + font-size: $normal-font-size; + font-style: italic; + color: #a9adb6; +} + +@mixin tab-text { + font-family: $sans-font; + font-size: $normal-font-size; + font-weight: 500; + color: $title-color; +} + +@mixin inherit-text { + font-family: inherit; + font-size: inherit; + color: inherit; + font-weight: inherit; +} + +@mixin undraggable { + user-drag: none; + user-select: none; + -moz-user-select: none; + -webkit-user-drag: none; + -webkit-user-select: none; + -ms-user-select: none; +} + +@mixin shadow { + box-shadow: 0px 4px 5px -4px rgba(0, 0, 0, 0.1); +} + +// CLASSES + +// FUNCTIONS +@function transparency($color, $transparency) { + @return color.adjust($color, $alpha: $transparency); +} + +@function lightness($color, $lightness) { + @return color.adjust($color, $lightness: $lightness); +} + +@function saturation($color, $saturation) { + @return color.adjust($color, $saturation: $saturation); +} :root { @include fonts.fonts; diff --git a/common-front/lib/wsHandler/useSubscribe.ts b/common-front/lib/wsHandler/useSubscribe.ts index 9c76e3adc..29e190aff 100644 --- a/common-front/lib/wsHandler/useSubscribe.ts +++ b/common-front/lib/wsHandler/useSubscribe.ts @@ -1,19 +1,13 @@ -import { useEffect, useRef } from "react"; -import { HandlerMessages } from "./HandlerMessages"; -import { SubscriptionTopic } from "./types"; -import { useWsHandler } from "."; -import { nanoid } from "nanoid"; +import { useEffect, useRef } from 'react'; +import { HandlerMessages } from './HandlerMessages'; +import { SubscriptionTopic } from './types'; +import { useWsHandler } from '.'; +import { nanoid } from 'nanoid'; export function useSubscribe<T extends SubscriptionTopic>( topic: T, - cb: (v: HandlerMessages[T]["response"]) => void + cb: (v: HandlerMessages[T]['response']) => void ) { - const callbackRef = useRef(cb); - - useEffect(() => { - callbackRef.current = cb; - }); - const handler = useWsHandler(); useEffect(() => { diff --git a/control-station/src/App.tsx b/control-station/src/App.tsx index e252cdf11..c54eaa759 100644 --- a/control-station/src/App.tsx +++ b/control-station/src/App.tsx @@ -1,65 +1,38 @@ -import { Outlet } from "react-router-dom"; -import "styles/global.scss"; -import "styles/scrollbars.scss"; -import styles from "./App.module.scss"; -import { Sidebar } from "components/Sidebar/Sidebar"; -import { ReactComponent as Wheel } from "assets/svg/wheel.svg"; -import { ReactComponent as Tube } from "assets/svg/tube.svg"; -import { ReactComponent as Cameras } from "assets/svg/cameras.svg"; -import { - Loader, - WsHandlerProvider, - createWsHandler, - useConfig, - useConnectionsStore, - useFetchBack, - useMeasurementsStore, - usePodDataStore, -} from "common"; +import { Outlet } from 'react-router-dom'; +import 'styles/global.scss'; +import 'styles/scrollbars.scss'; +import styles from './App.module.scss'; +import { Sidebar } from 'components/Sidebar/Sidebar'; +import { ReactComponent as Wheel } from 'assets/svg/wheel.svg'; +import { ReactComponent as Cameras } from 'assets/svg/cameras.svg'; +import { ReactComponent as TeamLogo } from 'assets/svg/team_logo.svg'; +import { SplashScreen, WsHandlerProvider, useLoadBackend } from 'common'; export const App = () => { - const setBackendConnection = useConnectionsStore(store => store.setBackendConnection); - const initPodData = usePodDataStore(store => store.initPodData); - const initMeasurements = useMeasurementsStore(store => store.initMeasurements); - const config = useConfig(); - const podDataDescriptionPromise = useFetchBack( - import.meta.env.PROD, - config.paths.podDataDescription - ); + const isProduction = import.meta.env.PROD; + const loadBackend = useLoadBackend(isProduction); - const WS_URL = import.meta.env.PROD - ? `${config.prodServer.ip}:${config.prodServer.port}/${config.paths.websocket}` - : `${config.devServer.ip}:${config.devServer.port}/${config.paths.websocket}`; return ( <div className={styles.appWrapper}> - <Loader - promises={[ - createWsHandler( - WS_URL, - true, - () => setBackendConnection(true), - () => setBackendConnection(false) - ), - podDataDescriptionPromise.then((adapter) => { - initPodData(adapter); - initMeasurements(adapter); - }), - ]} - LoadingView={<div>Loading</div>} - FailureView={<div>Failure</div>} - > - {([handler]) => ( - <WsHandlerProvider handler={handler}> - <Sidebar - items={[ - { path: "/vehicle", icon: <Wheel /> }, - { path: "/cameras", icon: <Cameras /> }, - ]} - /> - <Outlet /> - </WsHandlerProvider> - )} - </Loader> + {loadBackend.state === 'fulfilled' && ( + <WsHandlerProvider handler={loadBackend.wsHandler}> + <Sidebar + items={[ + { path: '/vehicle', icon: <Wheel /> }, + { path: '/cameras', icon: <Cameras /> }, + ]} + /> + <Outlet /> + </WsHandlerProvider> + )} + {loadBackend.state === 'pending' && ( + <SplashScreen> + <TeamLogo /> + </SplashScreen> + )} + {loadBackend.state === 'rejected' && ( + <div>{`${loadBackend.error}`}</div> + )} </div> ); }; diff --git a/control-station/src/assets/images/5696482.png b/control-station/src/assets/images/5696482.png deleted file mode 100644 index db23e0bcc..000000000 Binary files a/control-station/src/assets/images/5696482.png and /dev/null differ diff --git a/control-station/src/assets/images/7291204.png b/control-station/src/assets/images/7291204.png deleted file mode 100644 index 65672fb02..000000000 Binary files a/control-station/src/assets/images/7291204.png and /dev/null differ diff --git a/control-station/src/assets/images/EMS.png b/control-station/src/assets/images/EMS.png deleted file mode 100644 index 4731a6248..000000000 Binary files a/control-station/src/assets/images/EMS.png and /dev/null differ diff --git a/control-station/src/assets/images/HEMS.png b/control-station/src/assets/images/HEMS.png deleted file mode 100644 index 1c2cf754a..000000000 Binary files a/control-station/src/assets/images/HEMS.png and /dev/null differ diff --git a/control-station/src/assets/images/battery.png b/control-station/src/assets/images/battery.png deleted file mode 100644 index 19b0c079a..000000000 Binary files a/control-station/src/assets/images/battery.png and /dev/null differ diff --git a/control-station/src/assets/images/motor.png b/control-station/src/assets/images/motor.png deleted file mode 100644 index b648f2a78..000000000 Binary files a/control-station/src/assets/images/motor.png and /dev/null differ diff --git a/control-station/src/assets/images/pcb.png b/control-station/src/assets/images/pcb.png deleted file mode 100644 index 74dbd752f..000000000 Binary files a/control-station/src/assets/images/pcb.png and /dev/null differ diff --git a/control-station/src/assets/svg/EMS-wall.svg b/control-station/src/assets/svg/EMS-wall.svg new file mode 100644 index 000000000..eeebe1598 --- /dev/null +++ b/control-station/src/assets/svg/EMS-wall.svg @@ -0,0 +1,3 @@ +<svg width="16" height="117" viewBox="0 0 16 117" fill="none" xmlns="http://www.w3.org/2000/svg"> +<rect x="0.361328" y="117" width="117" height="15" rx="5" transform="rotate(-90 0.361328 117)" fill="#5894A7"/> +</svg> diff --git a/control-station/src/assets/svg/EMS.svg b/control-station/src/assets/svg/EMS.svg new file mode 100644 index 000000000..8d09ce724 --- /dev/null +++ b/control-station/src/assets/svg/EMS.svg @@ -0,0 +1,39 @@ +<svg width="35" height="117" viewBox="0 0 35 117" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M17.8301 106.971V90.2572H18.764C20.1448 90.2572 21.264 89.1379 21.264 87.7571L21.264 29.2429C21.264 27.8622 20.1448 26.7429 18.764 26.7429H17.8301V10.0286H21.264C28.8501 10.0286 34.9999 17.5118 34.9999 26.7429L34.9999 90.2572C34.9999 99.4882 28.8501 106.971 21.264 106.971H17.8301Z" fill="#C4D7E5"/> +<mask id="mask0_2144_8760" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="80" width="19" height="37"> +<rect x="0.660156" y="117" width="36.9871" height="17.5351" rx="2.5" transform="rotate(-90 0.660156 117)" fill="#A66223"/> +</mask> +<g mask="url(#mask0_2144_8760)"> +<rect x="0.660156" y="117" width="36.9871" height="17.5351" rx="2.5" transform="rotate(-90 0.660156 117)" fill="#FFD1B0"/> +<path d="M17.4648 117L17.4648 80.0129C17.0613 80.0129 16.7342 80.34 16.7342 80.7435L16.7342 116.269C16.7342 116.673 17.0613 117 17.4648 117Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M16.0034 117L16.0034 80.0129C15.5999 80.0129 15.2728 80.34 15.2728 80.7435L15.2728 116.269C15.2728 116.673 15.5999 117 16.0034 117Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M14.542 117L14.542 80.0129C14.1385 80.0129 13.8114 80.34 13.8114 80.7435L13.8114 116.269C13.8114 116.673 14.1385 117 14.542 117Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M13.0811 117L13.0811 80.0129C12.6775 80.0129 12.3504 80.34 12.3504 80.7435L12.3504 116.269C12.3504 116.673 12.6775 117 13.0811 117Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M11.6196 117L11.6196 80.0129C11.2161 80.0129 10.889 80.34 10.889 80.7435L10.889 116.269C10.889 116.673 11.2161 117 11.6196 117Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M10.1582 117L10.1582 80.0129C9.75469 80.0129 9.42757 80.34 9.42757 80.7435L9.42757 116.269C9.42757 116.673 9.75469 117 10.1582 117Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M8.69727 117L8.69727 80.0129C8.29375 80.0129 7.96664 80.34 7.96664 80.7435L7.96664 116.269C7.96664 116.673 8.29375 117 8.69727 117Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M7.23584 117L7.23584 80.0129C6.83232 80.0129 6.50521 80.34 6.50521 80.7435L6.50521 116.269C6.50521 116.673 6.83232 117 7.23584 117Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M5.77441 117L5.77441 80.0129C5.3709 80.0129 5.04378 80.34 5.04378 80.7435L5.04378 116.269C5.04378 116.673 5.3709 117 5.77441 117Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M4.31348 117L4.31348 80.0129C3.90996 80.0129 3.58285 80.34 3.58285 80.7435L3.58285 116.269C3.58285 116.673 3.90996 117 4.31348 117Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M2.85205 117L2.85205 80.0129C2.44854 80.0129 2.12142 80.34 2.12142 80.7435L2.12142 116.269C2.12142 116.673 2.44854 117 2.85205 117Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M1.39062 117L1.39063 80.0129C0.987109 80.0129 0.659995 80.34 0.659995 80.7435L0.659995 116.269C0.659995 116.673 0.987109 117 1.39062 117Z" fill="#EE7623" fill-opacity="0.5"/> +</g> +<mask id="mask1_2144_8760" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="-1" width="19" height="38"> +<rect x="0.660156" y="36.7715" width="36.9871" height="17.5351" rx="2.5" transform="rotate(-90 0.660156 36.7715)" fill="#A66223"/> +</mask> +<g mask="url(#mask1_2144_8760)"> +<rect x="0.660156" y="36.7715" width="36.9871" height="17.5351" rx="2.5" transform="rotate(-90 0.660156 36.7715)" fill="#FFD1B0"/> +<path d="M17.4648 36.7715L17.4648 -0.215614C17.0613 -0.215614 16.7342 0.1115 16.7342 0.515018L16.7342 36.0409C16.7342 36.4444 17.0613 36.7715 17.4648 36.7715Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M16.0034 36.7715L16.0034 -0.215614C15.5999 -0.215614 15.2728 0.1115 15.2728 0.515015L15.2728 36.0409C15.2728 36.4444 15.5999 36.7715 16.0034 36.7715Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M14.542 36.7715L14.542 -0.215614C14.1385 -0.215614 13.8114 0.1115 13.8114 0.515015L13.8114 36.0409C13.8114 36.4444 14.1385 36.7715 14.542 36.7715Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M13.0811 36.7715L13.0811 -0.215614C12.6775 -0.215614 12.3504 0.1115 12.3504 0.515015L12.3504 36.0409C12.3504 36.4444 12.6775 36.7715 13.0811 36.7715Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M11.6196 36.7715L11.6196 -0.215614C11.2161 -0.215614 10.889 0.1115 10.889 0.515015L10.889 36.0409C10.889 36.4444 11.2161 36.7715 11.6196 36.7715Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M10.1582 36.7715L10.1582 -0.215614C9.75469 -0.215614 9.42757 0.1115 9.42757 0.515015L9.42757 36.0409C9.42757 36.4444 9.75469 36.7715 10.1582 36.7715Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M8.69727 36.7715L8.69727 -0.215614C8.29375 -0.215614 7.96664 0.1115 7.96664 0.515015L7.96664 36.0409C7.96664 36.4444 8.29375 36.7715 8.69727 36.7715Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M7.23584 36.7715L7.23584 -0.215614C6.83232 -0.215614 6.50521 0.1115 6.50521 0.515015L6.50521 36.0409C6.50521 36.4444 6.83232 36.7715 7.23584 36.7715Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M5.77441 36.7715L5.77441 -0.215614C5.3709 -0.215614 5.04378 0.1115 5.04378 0.515015L5.04378 36.0409C5.04378 36.4444 5.3709 36.7715 5.77441 36.7715Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M4.31348 36.7715L4.31348 -0.215614C3.90996 -0.215614 3.58285 0.1115 3.58285 0.515015L3.58285 36.0409C3.58285 36.4444 3.90996 36.7715 4.31348 36.7715Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M2.85205 36.7715L2.85205 -0.215614C2.44854 -0.215614 2.12142 0.1115 2.12142 0.515015L2.12142 36.0409C2.12142 36.4444 2.44854 36.7715 2.85205 36.7715Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M1.39062 36.7715L1.39063 -0.215614C0.987109 -0.215614 0.659995 0.1115 0.659995 0.515015L0.659995 36.0409C0.659995 36.4444 0.987109 36.7715 1.39062 36.7715Z" fill="#EE7623" fill-opacity="0.5"/> +</g> +</svg> diff --git a/control-station/src/assets/svg/HEMS-wall.svg b/control-station/src/assets/svg/HEMS-wall.svg new file mode 100644 index 000000000..854702b9d --- /dev/null +++ b/control-station/src/assets/svg/HEMS-wall.svg @@ -0,0 +1,3 @@ +<svg width="120" height="16" viewBox="0 0 120 16" fill="none" xmlns="http://www.w3.org/2000/svg"> +<rect y="0.172852" width="120" height="15" rx="5" fill="#5894A7"/> +</svg> diff --git a/control-station/src/assets/svg/HEMS.svg b/control-station/src/assets/svg/HEMS.svg new file mode 100644 index 000000000..0c991ef3d --- /dev/null +++ b/control-station/src/assets/svg/HEMS.svg @@ -0,0 +1,41 @@ +<svg width="120" height="30" viewBox="0 0 120 30" fill="none" xmlns="http://www.w3.org/2000/svg"> +<mask id="mask0_2144_8649" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="60" height="18"> +<rect y="0.657959" width="59.027" height="16.8533" rx="2.5" fill="#A66223"/> +</mask> +<g mask="url(#mask0_2144_8649)"> +<rect y="0.657959" width="59.027" height="16.8533" rx="2.5" fill="#FFD1B0"/> +<path d="M0 16.863H59.027C59.027 17.221 58.7368 17.5112 58.3788 17.5112H0.648202C0.290209 17.5112 0 17.221 0 16.863Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M0 15.5667H59.027C59.027 15.9246 58.7368 16.2149 58.3788 16.2149H0.648205C0.290212 16.2149 0 15.9246 0 15.5667Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M0 14.2703H59.027C59.027 14.6283 58.7368 14.9185 58.3788 14.9185H0.648205C0.290212 14.9185 0 14.6283 0 14.2703Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M0 12.9739H59.027C59.027 13.3319 58.7368 13.6221 58.3788 13.6221H0.648205C0.290212 13.6221 0 13.3319 0 12.9739Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M0 11.6775H59.027C59.027 12.0355 58.7368 12.3257 58.3788 12.3257H0.648205C0.290212 12.3257 0 12.0355 0 11.6775Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M0 10.3811H59.027C59.027 10.7391 58.7368 11.0293 58.3788 11.0293H0.648205C0.290212 11.0293 0 10.7391 0 10.3811Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M0 9.08472H59.027C59.027 9.44271 58.7368 9.73292 58.3788 9.73292H0.648205C0.290212 9.73292 0 9.44271 0 9.08472Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M0 7.78809H59.027C59.027 8.14608 58.7368 8.43629 58.3788 8.43629H0.648205C0.290213 8.43629 0 8.14608 0 7.78809Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M0 6.4917H59.027C59.027 6.84969 58.7368 7.1399 58.3788 7.1399H0.648205C0.290212 7.1399 0 6.84969 0 6.4917Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M0 5.19531H59.027C59.027 5.55331 58.7368 5.84352 58.3788 5.84352H0.648205C0.290212 5.84352 0 5.55331 0 5.19531Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M0 3.89893H59.027C59.027 4.25692 58.7368 4.54713 58.3788 4.54713H0.648205C0.290212 4.54713 0 4.25692 0 3.89893Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M0 2.60254H59.027C59.027 2.96053 58.7368 3.25074 58.3788 3.25074H0.648205C0.290212 3.25074 0 2.96053 0 2.60254Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M0 1.30615H59.027C59.027 1.66415 58.7368 1.95436 58.3788 1.95436H0.648205C0.290212 1.95436 0 1.66415 0 1.30615Z" fill="#EE7623" fill-opacity="0.5"/> +</g> +<mask id="mask1_2144_8649" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="60" y="0" width="60" height="18"> +<rect x="60.9731" y="0.657959" width="59.027" height="16.8533" rx="2.5" fill="#A66223"/> +</mask> +<g mask="url(#mask1_2144_8649)"> +<rect x="60.9731" y="0.657959" width="59.027" height="16.8533" rx="2.5" fill="#FFD1B0"/> +<path d="M60.9731 16.863H120C120 17.221 119.71 17.5112 119.352 17.5112H61.6213C61.2634 17.5112 60.9731 17.221 60.9731 16.863Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M60.9731 15.5667H120C120 15.9246 119.71 16.2149 119.352 16.2149H61.6213C61.2634 16.2149 60.9731 15.9246 60.9731 15.5667Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M60.9731 14.2703H120C120 14.6283 119.71 14.9185 119.352 14.9185H61.6213C61.2634 14.9185 60.9731 14.6283 60.9731 14.2703Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M60.9731 12.9739H120C120 13.3319 119.71 13.6221 119.352 13.6221H61.6213C61.2634 13.6221 60.9731 13.3319 60.9731 12.9739Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M60.9731 11.6775H120C120 12.0355 119.71 12.3257 119.352 12.3257H61.6213C61.2634 12.3257 60.9731 12.0355 60.9731 11.6775Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M60.9731 10.3811H120C120 10.7391 119.71 11.0293 119.352 11.0293H61.6213C61.2634 11.0293 60.9731 10.7391 60.9731 10.3811Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M60.9731 9.08472H120C120 9.44271 119.71 9.73292 119.352 9.73292H61.6213C61.2634 9.73292 60.9731 9.44271 60.9731 9.08472Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M60.9731 7.78809H120C120 8.14608 119.71 8.43629 119.352 8.43629H61.6213C61.2634 8.43629 60.9731 8.14608 60.9731 7.78809Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M60.9731 6.4917H120C120 6.84969 119.71 7.1399 119.352 7.1399H61.6213C61.2634 7.1399 60.9731 6.84969 60.9731 6.4917Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M60.9731 5.19531H120C120 5.55331 119.71 5.84352 119.352 5.84352H61.6213C61.2634 5.84352 60.9731 5.55331 60.9731 5.19531Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M60.9731 3.89893H120C120 4.25692 119.71 4.54713 119.352 4.54713H61.6213C61.2634 4.54713 60.9731 4.25692 60.9731 3.89893Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M60.9731 2.60254H120C120 2.96053 119.71 3.25074 119.352 3.25074H61.6213C61.2634 3.25074 60.9731 2.96053 60.9731 2.60254Z" fill="#EE7623" fill-opacity="0.5"/> +<path d="M60.9731 1.30615H120C120 1.66415 119.71 1.95436 119.352 1.95436H61.6213C61.2634 1.95436 60.9731 1.66415 60.9731 1.30615Z" fill="#EE7623" fill-opacity="0.5"/> +</g> +<path fill-rule="evenodd" clip-rule="evenodd" d="M107.027 17.5112H12.9731V25.9379L16.865 29.8271H103.135L107.027 25.9379V17.5112ZM19.6542 26.975C20.8364 26.975 21.7948 26.0173 21.7948 24.8359C21.7948 23.6546 20.8364 22.6969 19.6542 22.6969C18.472 22.6969 17.5137 23.6546 17.5137 24.8359C17.5137 26.0173 18.472 26.975 19.6542 26.975ZM100.346 26.975C99.1639 26.975 98.2056 26.0173 98.2056 24.8359C98.2056 23.6546 99.1639 22.6969 100.346 22.6969C101.528 22.6969 102.487 23.6546 102.487 24.8359C102.487 26.0173 101.528 26.975 100.346 26.975Z" fill="#C4D7E5"/> +</svg> diff --git a/control-station/src/assets/svg/XT90.svg b/control-station/src/assets/svg/XT90.svg new file mode 100644 index 000000000..3f203b28f --- /dev/null +++ b/control-station/src/assets/svg/XT90.svg @@ -0,0 +1,13 @@ +<svg width="17" height="41" viewBox="0 0 17 41" fill="none" xmlns="http://www.w3.org/2000/svg"> +<g clip-path="url(#clip0_2204_3485)"> +<rect y="0.399902" width="17" height="29.6296" fill="#C4D7E5"/> +<ellipse cx="8.5" cy="30.3999" rx="8.5" ry="10" fill="#C4D7E5"/> +<ellipse cx="8.5" cy="28.9713" rx="4.25" ry="4.28571" fill="#3C7A8D"/> +<ellipse cx="8.5" cy="11.8284" rx="4.25" ry="4.28571" fill="#3C7A8D"/> +</g> +<defs> +<clipPath id="clip0_2204_3485"> +<rect width="17" height="40" fill="white" transform="translate(0 0.399902)"/> +</clipPath> +</defs> +</svg> diff --git a/control-station/src/assets/svg/battery-filled.svg b/control-station/src/assets/svg/battery-filled.svg index 12416d508..5c8e862d9 100644 --- a/control-station/src/assets/svg/battery-filled.svg +++ b/control-station/src/assets/svg/battery-filled.svg @@ -1,3 +1,4 @@ -<svg width="10" height="15" viewBox="0 0 10 15" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M10 14.25V3.25C10 2.83579 9.66421 2.5 9.25 2.5H7.5V0.75C7.5 0.335786 7.16421 0 6.75 0H3.25C2.83579 0 2.5 0.335786 2.5 0.75V2.5H0.75C0.335786 2.5 0 2.83579 0 3.25V14.25C0 14.6642 0.335786 15 0.75 15H9.25C9.66421 15 10 14.6642 10 14.25Z" fill="black"/> -</svg> +<svg width="10" height="15" viewBox="0 0 10 15" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> + <path + d="M10 14.25V3.25C10 2.83579 9.66421 2.5 9.25 2.5H7.5V0.75C7.5 0.335786 7.16421 0 6.75 0H3.25C2.83579 0 2.5 0.335786 2.5 0.75V2.5H0.75C0.335786 2.5 0 2.83579 0 3.25V14.25C0 14.6642 0.335786 15 0.75 15H9.25C9.66421 15 10 14.6642 10 14.25Z" /> +</svg> \ No newline at end of file diff --git a/control-station/src/assets/svg/brake.svg b/control-station/src/assets/svg/brake.svg deleted file mode 100644 index a0046e9df..000000000 --- a/control-station/src/assets/svg/brake.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg width="3em" height="3em" viewBox="0 0 45 55" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M42.4682 39.5937L42.3781 40.7566C42.1634 44.1527 40.3139 47.1141 37.6081 49.1937C37.5484 49.2402 37.4872 49.2864 37.4359 49.3251L37.4185 49.3383C37.3988 49.3541 37.3645 49.3808 37.3199 49.4115C37.2214 49.483 37.122 49.5529 37.019 49.6228C34.4027 51.4308 31.0519 52.5 27.4213 52.5C24.9021 52.5 22.5145 51.9852 20.4145 51.064V51.3464L18.0851 49.8005L18.085 49.8004L18.0849 49.8004L18.0845 49.8001L18.0826 49.7989L18.0754 49.794L18.0476 49.7756L17.9481 49.7096C17.8655 49.6549 17.7552 49.5819 17.6441 49.5085L17.6432 49.5079L17.3354 49.3045L17.2317 49.2358L17.1989 49.214L17.1838 49.2039L17.1828 49.2032C16.1036 48.4855 14.7028 47.0591 13.9151 46.2232C13.5456 45.8443 13.1757 45.4386 12.8126 45.0024L8.7707 40.2889L7.97138 39.4052L7.97073 39.4045L3.60161 34.5806C3.60134 34.5803 3.60107 34.58 3.60079 34.5797C3.60076 34.5797 3.60073 34.5797 3.60069 34.5796C1.89251 32.696 2.22239 29.8731 4.23757 28.3652C5.09941 27.7184 6.1249 27.418 7.12275 27.418C8.02139 27.418 8.92938 27.6597 9.71406 28.1544L9.82768 28.226L9.86913 28.264C9.91028 28.2948 9.97586 28.3448 10.042 28.3996C10.1058 28.4507 10.16 28.4998 10.2028 28.5408L12.3146 30.4612L11.3054 31.5709L12.3146 30.4612L12.4499 30.5842L12.4499 30.5842L12.4562 30.5899L12.7861 30.8934V10.8338C12.7861 8.42229 14.9025 6.69822 17.1885 6.69822H17.2689C18.1551 6.69822 19.0152 6.95801 19.7362 7.41661V5.63553C19.7362 3.22542 21.851 1.5 24.1344 1.5H24.2168C26.5012 1.5 28.6171 3.22466 28.6171 5.63553V6.00308C29.3361 5.54901 30.1915 5.29195 31.0717 5.29195H31.1541C33.4387 5.29195 35.5523 7.02064 35.5523 9.42965V11.1201C36.2745 10.661 37.1361 10.4009 38.0236 10.4009H38.1018C40.3924 10.4009 42.5 12.131 42.5 14.5384V34.857L42.8749 34.3455L42.4682 39.5937Z" fill="#EE8735" stroke="white" stroke-width="3"/> -</svg> diff --git a/control-station/src/assets/svg/breakIcon.svg b/control-station/src/assets/svg/breakIcon.svg deleted file mode 100644 index be0d0f2bc..000000000 --- a/control-station/src/assets/svg/breakIcon.svg +++ /dev/null @@ -1,3 +0,0 @@ -<svg width="2em" height="2em" viewBox="0 0 29 34" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> -<path d="M25 25.2914C25 25.4766 24.9916 25.6602 24.9787 25.8425L24.9815 25.8384L24.9201 26.6698C24.7988 28.7191 23.7326 30.5561 22.0883 31.88C22.0483 31.9126 22.0069 31.9453 21.9655 31.9781C21.9542 31.9861 21.9456 31.9956 21.9327 32.0038C21.8686 32.0529 21.8042 32.1004 21.7372 32.148C20.1458 33.302 18.0833 34 15.8252 34C13.6457 34 11.6446 33.3496 10.0774 32.2637V32.265C10.0774 32.265 9.92883 32.1617 9.77759 32.057C9.62635 31.9521 9.47493 31.8474 9.47349 31.846C8.8542 31.416 7.97632 30.4881 7.43103 29.8811C7.19414 29.6267 6.95868 29.356 6.72877 29.0661L3.98408 25.7106L3.43456 25.0738L0.481393 21.6556C-0.259398 20.7998 -0.130933 19.5221 0.768302 18.8173C1.16079 18.5083 1.63611 18.3586 2.10997 18.3586C2.53817 18.3586 2.96209 18.4797 3.32036 18.7165C3.32321 18.7192 3.32892 18.7233 3.33462 18.7274C3.35888 18.7464 3.40315 18.7806 3.44311 18.8159C3.46309 18.8322 3.48163 18.8499 3.49735 18.8661L4.9361 20.2377L5.02749 20.3249L6.52466 21.7686C6.68182 21.9196 6.95007 21.8135 6.95007 21.6011V5.54891C6.95007 4.52171 7.83207 3.68208 8.91118 3.68208H8.96548C10.0417 3.68208 10.9223 4.52171 10.9223 5.54891V15.0303H10.928V15.3433C10.928 15.5611 11.145 15.7324 11.3834 15.6713C11.5403 15.6318 11.6432 15.4863 11.6432 15.3312V15.0303H11.6461V1.86683C11.6461 0.83963 12.528 0 13.6043 0H13.66C14.7375 0 15.6197 0.83963 15.6197 1.86683V15.3869C15.6439 15.5693 15.8224 15.7094 16.0308 15.6809C16.2092 15.6563 16.3334 15.4984 16.3334 15.3257V4.55433C16.3334 3.52696 17.2169 2.68596 18.2917 2.68596H18.3474C19.4235 2.68596 20.3056 3.52696 20.3056 4.55433V15.3433C20.3056 15.5611 20.5239 15.7324 20.761 15.6713C20.9194 15.6318 21.0221 15.4863 21.0221 15.3312V15.0303H21.0278V8.17303C21.0278 7.14583 21.91 6.30483 22.9889 6.30483H23.0418C24.1209 6.30483 25 7.14583 25 8.17303V25.2914Z" fill="currentColor"/> -</svg> diff --git a/control-station/src/assets/svg/ems.svg b/control-station/src/assets/svg/ems.svg deleted file mode 100644 index 3d90bd782..000000000 --- a/control-station/src/assets/svg/ems.svg +++ /dev/null @@ -1,47 +0,0 @@ -<svg width="109" height="152" viewBox="0 0 109 152" fill="none" xmlns="http://www.w3.org/2000/svg"> -<g opacity="0.9" clip-path="url(#clip0_1797_7693)"> -<path d="M85.5 138.714V116.571H87.7C89.0807 116.571 90.2 115.452 90.2 114.071V34.9286C90.2 33.5479 89.0807 32.4286 87.7 32.4286H85.5L85.5 10.2857H90.2C100.583 10.2857 109 20.1994 109 32.4286V116.571C109 128.801 100.583 138.714 90.2 138.714H85.5Z" fill="#C4D7E5"/> -<mask id="mask0_1797_7693" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="62" y="103" width="24" height="49"> -<rect x="62" y="152" width="49" height="24" rx="2.5" transform="rotate(-90 62 152)" fill="#A66223"/> -</mask> -<g mask="url(#mask0_1797_7693)"> -<rect x="62" y="152" width="49" height="24" rx="2.5" transform="rotate(-90 62 152)" fill="#FFD1B0"/> -<path d="M85 152V103C84.4477 103 84 103.448 84 104V151C84 151.552 84.4477 152 85 152Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M83 152V103C82.4477 103 82 103.448 82 104V151C82 151.552 82.4477 152 83 152Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M81 152V103C80.4477 103 80 103.448 80 104V151C80 151.552 80.4477 152 81 152Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M79 152V103C78.4477 103 78 103.448 78 104V151C78 151.552 78.4477 152 79 152Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M77 152V103C76.4477 103 76 103.448 76 104V151C76 151.552 76.4477 152 77 152Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M75 152V103C74.4477 103 74 103.448 74 104V151C74 151.552 74.4477 152 75 152Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M73 152V103C72.4477 103 72 103.448 72 104V151C72 151.552 72.4477 152 73 152Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M71 152V103C70.4477 103 70 103.448 70 104V151C70 151.552 70.4477 152 71 152Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M69 152V103C68.4477 103 68 103.448 68 104V151C68 151.552 68.4477 152 69 152Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M67 152V103C66.4477 103 66 103.448 66 104V151C66 151.552 66.4477 152 67 152Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M65 152V103C64.4477 103 64 103.448 64 104V151C64 151.552 64.4477 152 65 152Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M63 152V103C62.4477 103 62 103.448 62 104V151C62 151.552 62.4477 152 63 152Z" fill="#EE7623" fill-opacity="0.5"/> -</g> -<mask id="mask1_1797_7693" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="62" y="-4" width="24" height="50"> -<rect x="62" y="45.7144" width="49" height="24" rx="2.5" transform="rotate(-90 62 45.7144)" fill="#A66223"/> -</mask> -<g mask="url(#mask1_1797_7693)"> -<rect x="62" y="45.7144" width="49" height="24" rx="2.5" transform="rotate(-90 62 45.7144)" fill="#FFD1B0"/> -<path d="M85 45.7144V-3.28564C84.4477 -3.28564 84 -2.83793 84 -2.28564V44.7144C84 45.2666 84.4477 45.7144 85 45.7144Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M83 45.7144V-3.28564C82.4477 -3.28564 82 -2.83793 82 -2.28564V44.7144C82 45.2666 82.4477 45.7144 83 45.7144Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M81 45.7144V-3.28564C80.4477 -3.28564 80 -2.83793 80 -2.28564V44.7144C80 45.2666 80.4477 45.7144 81 45.7144Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M79 45.7144V-3.28564C78.4477 -3.28564 78 -2.83793 78 -2.28564V44.7144C78 45.2666 78.4477 45.7144 79 45.7144Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M77 45.7144V-3.28564C76.4477 -3.28564 76 -2.83793 76 -2.28564V44.7144C76 45.2666 76.4477 45.7144 77 45.7144Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M75 45.7144V-3.28564C74.4477 -3.28564 74 -2.83793 74 -2.28564V44.7144C74 45.2666 74.4477 45.7144 75 45.7144Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M73 45.7144V-3.28564C72.4477 -3.28564 72 -2.83793 72 -2.28564V44.7144C72 45.2666 72.4477 45.7144 73 45.7144Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M71 45.7144V-3.28564C70.4477 -3.28564 70 -2.83793 70 -2.28564V44.7144C70 45.2666 70.4477 45.7144 71 45.7144Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M69 45.7144V-3.28564C68.4477 -3.28564 68 -2.83793 68 -2.28564V44.7144C68 45.2666 68.4477 45.7144 69 45.7144Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M67 45.7144V-3.28564C66.4477 -3.28564 66 -2.83793 66 -2.28564V44.7144C66 45.2666 66.4477 45.7144 67 45.7144Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M65 45.7144V-3.28564C64.4477 -3.28564 64 -2.83793 64 -2.28564V44.7144C64 45.2666 64.4477 45.7144 65 45.7144Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M63 45.7144V-3.28564C62.4477 -3.28564 62 -2.83793 62 -2.28564V44.7144C62 45.2666 62.4477 45.7144 63 45.7144Z" fill="#EE7623" fill-opacity="0.5"/> -</g> -<rect y="152" width="155" height="15" rx="5" transform="rotate(-90 0 152)" fill="#5894A7"/> -</g> -<defs> -<clipPath id="clip0_1797_7693"> -<rect width="155" height="109" fill="white" transform="matrix(0 -1 1 0 0 152)"/> -</clipPath> -</defs> -</svg> diff --git a/control-station/src/assets/svg/hems.svg b/control-station/src/assets/svg/hems.svg deleted file mode 100644 index d81d1adaf..000000000 --- a/control-station/src/assets/svg/hems.svg +++ /dev/null @@ -1,49 +0,0 @@ -<svg width="185" height="109" viewBox="0 0 185 109" fill="none" xmlns="http://www.w3.org/2000/svg"> -<g clip-path="url(#clip0_1797_7696)"> -<mask id="mask0_1797_7696" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="64" width="91" height="26"> -<rect y="64" width="91" height="26" rx="2.5" fill="#A66223"/> -</mask> -<g mask="url(#mask0_1797_7696)"> -<rect y="64" width="91" height="26" rx="2.5" fill="#FFD1B0"/> -<path d="M0 89H91C91 89.5523 90.5523 90 90 90H1C0.447715 90 0 89.5523 0 89Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M0 87H91C91 87.5523 90.5523 88 90 88H1C0.447715 88 0 87.5523 0 87Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M0 85H91C91 85.5523 90.5523 86 90 86H1C0.447715 86 0 85.5523 0 85Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M0 83H91C91 83.5523 90.5523 84 90 84H1C0.447715 84 0 83.5523 0 83Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M0 81H91C91 81.5523 90.5523 82 90 82H1C0.447715 82 0 81.5523 0 81Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M0 79H91C91 79.5523 90.5523 80 90 80H1C0.447715 80 0 79.5523 0 79Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M0 77H91C91 77.5523 90.5523 78 90 78H1C0.447715 78 0 77.5523 0 77Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M0 75H91C91 75.5523 90.5523 76 90 76H1C0.447715 76 0 75.5523 0 75Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M0 73H91C91 73.5523 90.5523 74 90 74H1C0.447715 74 0 73.5523 0 73Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M0 71H91C91 71.5523 90.5523 72 90 72H1C0.447715 72 0 71.5523 0 71Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M0 69H91C91 69.5523 90.5523 70 90 70H1C0.447715 70 0 69.5523 0 69Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M0 67H91C91 67.5523 90.5523 68 90 68H1C0.447715 68 0 67.5523 0 67Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M0 65H91C91 65.5523 90.5523 66 90 66H1C0.447715 66 0 65.5523 0 65Z" fill="#EE7623" fill-opacity="0.5"/> -</g> -<mask id="mask1_1797_7696" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="94" y="64" width="91" height="26"> -<rect x="94" y="64" width="91" height="26" rx="2.5" fill="#A66223"/> -</mask> -<g mask="url(#mask1_1797_7696)"> -<rect x="94" y="64" width="91" height="26" rx="2.5" fill="#FFD1B0"/> -<path d="M94 89H185C185 89.5523 184.552 90 184 90H95C94.4477 90 94 89.5523 94 89Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M94 87H185C185 87.5523 184.552 88 184 88H95C94.4477 88 94 87.5523 94 87Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M94 85H185C185 85.5523 184.552 86 184 86H95C94.4477 86 94 85.5523 94 85Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M94 83H185C185 83.5523 184.552 84 184 84H95C94.4477 84 94 83.5523 94 83Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M94 81H185C185 81.5523 184.552 82 184 82H95C94.4477 82 94 81.5523 94 81Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M94 79H185C185 79.5523 184.552 80 184 80H95C94.4477 80 94 79.5523 94 79Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M94 77H185C185 77.5523 184.552 78 184 78H95C94.4477 78 94 77.5523 94 77Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M94 75H185C185 75.5523 184.552 76 184 76H95C94.4477 76 94 75.5523 94 75Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M94 73H185C185 73.5523 184.552 74 184 74H95C94.4477 74 94 73.5523 94 73Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M94 71H185C185 71.5523 184.552 72 184 72H95C94.4477 72 94 71.5523 94 71Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M94 69H185C185 69.5523 184.552 70 184 70H95C94.4477 70 94 69.5523 94 69Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M94 67H185C185 67.5523 184.552 68 184 68H95C94.4477 68 94 67.5523 94 67Z" fill="#EE7623" fill-opacity="0.5"/> -<path d="M94 65H185C185 65.5523 184.552 66 184 66H95C94.4477 66 94 65.5523 94 65Z" fill="#EE7623" fill-opacity="0.5"/> -</g> -<path fill-rule="evenodd" clip-rule="evenodd" d="M165 90H20V103L26 109H159L165 103V90ZM30.3 104.6C32.1225 104.6 33.6 103.123 33.6 101.3C33.6 99.4775 32.1225 98 30.3 98C28.4775 98 27 99.4775 27 101.3C27 103.123 28.4775 104.6 30.3 104.6ZM154.7 104.6C152.877 104.6 151.4 103.123 151.4 101.3C151.4 99.4775 152.877 98 154.7 98C156.523 98 158 99.4775 158 101.3C158 103.123 156.523 104.6 154.7 104.6Z" fill="#C4D7E5"/> -<rect width="185" height="15" rx="5" fill="#5894A7"/> -</g> -<defs> -<clipPath id="clip0_1797_7696"> -<rect width="185" height="109" fill="white"/> -</clipPath> -</defs> -</svg> diff --git a/control-station/src/assets/svg/motor.svg b/control-station/src/assets/svg/motor.svg deleted file mode 100644 index 0e0b981d9..000000000 --- a/control-station/src/assets/svg/motor.svg +++ /dev/null @@ -1,21 +0,0 @@ -<svg width="14em" height="2.2em" viewBox="0 0 140 22" fill="none" xmlns="http://www.w3.org/2000/svg"> -<rect width="14.8241" height="11" transform="translate(88)" fill="#E3B682"/> -<rect width="14.8241" height="11" transform="translate(95.2312 11)" fill="#C38741"/> -<rect width="14.8241" height="11" transform="translate(102.824)" fill="#7D5527"/> -<rect width="14.8241" height="11" transform="translate(110.055 11)" fill="#E3B682"/> -<rect width="14.8241" height="11" transform="translate(117.648)" fill="#C38741"/> -<rect width="14.8241" height="11" transform="translate(124.879 11)" fill="#7D5527"/> -<rect width="14.8241" height="11" fill="#E3B682"/> -<rect width="14.8241" height="11" transform="translate(7.2312 11)" fill="#C38741"/> -<rect width="14.8241" height="11" transform="translate(14.8242)" fill="#7D5527"/> -<rect width="14.8241" height="11" transform="translate(22.0554 11)" fill="#E3B682"/> -<rect width="14.8241" height="11" transform="translate(29.6482)" fill="#C38741"/> -<rect width="14.8241" height="11" transform="translate(36.8794 11)" fill="#7D5527"/> -<rect width="14.8241" height="11" transform="translate(44)" fill="#E3B682"/> -<rect width="14.8241" height="11" transform="translate(51.2312 11)" fill="#C38741"/> -<rect width="14.8241" height="11" transform="translate(58.8242)" fill="#7D5527"/> -<rect width="14.8241" height="11" transform="translate(66.0554 11)" fill="#E3B682"/> -<rect width="14.8241" height="11" transform="translate(73.6482)" fill="#C38741"/> -<rect width="14.8241" height="11" transform="translate(80.8794 11)" fill="#7D5527"/> -<rect width="140" height="22" fill="#1CA0FF" fill-opacity="0.12" style="mix-blend-mode:hue"/> -</svg> diff --git a/control-station/src/assets/svg/open_switch.svg b/control-station/src/assets/svg/open_switch.svg deleted file mode 100644 index 46bb8a44c..000000000 --- a/control-station/src/assets/svg/open_switch.svg +++ /dev/null @@ -1,7 +0,0 @@ -<svg width="6em" height="3em" viewBox="0 0 87 26" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M2 22H31" stroke="currentColor" stroke-width="4" stroke-linecap="round"/> -<path d="M85 22H56" stroke="currentColor" stroke-width="4" stroke-linecap="round"/> -<circle cx="29" cy="22" r="4" fill="currentColor"/> -<circle cx="4" cy="4" r="4" transform="matrix(-1 0 0 1 61 18)" fill="currentColor"/> -<line x1="29" y1="22.1716" x2="49.1716" y2="2" stroke="currentColor" stroke-width="4" stroke-linecap="round"/> -</svg> diff --git a/control-station/src/assets/svg/pitch-rotation.svg b/control-station/src/assets/svg/pitch-rotation.svg index 84af0a227..a6f51550e 100644 --- a/control-station/src/assets/svg/pitch-rotation.svg +++ b/control-station/src/assets/svg/pitch-rotation.svg @@ -1,4 +1,8 @@ <svg width="73" height="28" viewBox="0 0 73 28" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M9.15161 10.9812L47.8571 2.54919C53.691 1.27831 63.3518 14.338 58.64 15.3644C53.9282 16.3909 4.7888 27.0956 4.7888 27.0956C0.0766335 28.1221 3.31772 12.252 9.15161 10.9812Z" fill="black"/> -<path d="M66.2583 25.4932C71.7583 17.8265 71.7583 10.1598 66.2583 2.49316M66.2583 2.49316L64.2583 7.42174M66.2583 2.49316L71.2583 4.13602" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/> -</svg> + <path + d="M9.15161 10.9812L47.8571 2.54919C53.691 1.27831 63.3518 14.338 58.64 15.3644C53.9282 16.3909 4.7888 27.0956 4.7888 27.0956C0.0766335 28.1221 3.31772 12.252 9.15161 10.9812Z" + fill="currentColor" /> + <path + d="M66.2583 25.4932C71.7583 17.8265 71.7583 10.1598 66.2583 2.49316M66.2583 2.49316L64.2583 7.42174M66.2583 2.49316L71.2583 4.13602" + stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" /> +</svg> \ No newline at end of file diff --git a/control-station/src/assets/svg/plugged-icon.svg b/control-station/src/assets/svg/plugged-icon.svg new file mode 100644 index 000000000..18b06ee64 --- /dev/null +++ b/control-station/src/assets/svg/plugged-icon.svg @@ -0,0 +1,19 @@ +<svg width="13" height="12" viewBox="0 0 13 12" fill="none" xmlns="http://www.w3.org/2000/svg"> + <g clip-path="url(#clip0_1199_85)"> + <path + d="M3.83322 6L6.33322 8.5L5.58322 9.25C5.42005 9.41855 5.22497 9.55295 5.00935 9.64536C4.79372 9.73776 4.56186 9.78633 4.32727 9.78824C4.09269 9.79014 3.86007 9.74534 3.64297 9.65645C3.42588 9.56756 3.22864 9.43635 3.06276 9.27046C2.89688 9.10458 2.76566 8.90734 2.67677 8.69025C2.58788 8.47315 2.54308 8.24053 2.54498 8.00594C2.54688 7.77136 2.59546 7.5395 2.68786 7.32387C2.78027 7.10825 2.91467 6.91317 3.08322 6.75L3.83322 6Z" + stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" /> + <path + d="M8.83325 5.99997L6.33325 3.49997L7.08325 2.74997C7.24642 2.58141 7.4415 2.44702 7.65712 2.35461C7.87275 2.2622 8.10461 2.21363 8.3392 2.21173C8.57378 2.20982 8.8064 2.25462 9.0235 2.34352C9.2406 2.43241 9.43783 2.56362 9.60371 2.72951C9.7696 2.89539 9.90081 3.09262 9.9897 3.30972C10.0786 3.52682 10.1234 3.75944 10.1215 3.99402C10.1196 4.22861 10.071 4.46047 9.97861 4.67609C9.8862 4.89172 9.75181 5.0868 9.58325 5.24997L8.83325 5.99997Z" + stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" /> + <path d="M1.83325 10.5L3.08325 9.25" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" /> + <path d="M9.58325 2.75L10.8333 1.5" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" /> + <path d="M5.33325 5.5L4.33325 6.5" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" /> + <path d="M6.83325 7L5.83325 8" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" /> + </g> + <defs> + <clipPath id="clip0_1199_85"> + <rect width="12" height="12" fill="currentColor" transform="translate(0.333252)" /> + </clipPath> + </defs> +</svg> \ No newline at end of file diff --git a/control-station/src/assets/svg/pressure-filled.svg b/control-station/src/assets/svg/pressure-filled.svg new file mode 100644 index 000000000..6d3ba7237 --- /dev/null +++ b/control-station/src/assets/svg/pressure-filled.svg @@ -0,0 +1,12 @@ +<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg"> + <g clip-path="url(#clip0_1663_11611)"> + <path fill-rule="evenodd" clip-rule="evenodd" + d="M10 5C10 7.76142 7.76142 10 5 10C2.23858 10 0 7.76142 0 5C0 2.23858 2.23858 0 5 0C7.76142 0 10 2.23858 10 5ZM9.33449 5.86217C9.18557 6.61081 8.84572 7.3062 8.35036 7.88204C8.23098 8.02082 8.02005 8.02005 7.89061 7.89061C7.76117 7.76117 7.76245 7.55217 7.87998 7.41182C8.28359 6.92988 8.56098 6.35284 8.68431 5.73284C8.69311 5.68858 8.70111 5.64423 8.70829 5.59979L8.51879 5.56914C8.38249 5.5471 8.29086 5.41874 8.30257 5.28116C8.31788 5.10128 8.31848 4.92045 8.30434 4.74047C8.29353 4.60282 8.386 4.47506 8.52244 4.45391L8.71215 4.4245C8.6665 4.13006 8.58576 3.84059 8.47054 3.56243C8.35849 3.29192 8.21582 3.03689 8.04625 2.80189L7.89054 2.91425C7.77857 2.99504 7.62301 2.96906 7.53402 2.8635C7.41765 2.72548 7.2902 2.59719 7.15294 2.47992C7.04796 2.39023 7.02301 2.23451 7.10453 2.12308L7.21789 1.96811C7.17497 1.93672 7.13133 1.90619 7.08699 1.87656C6.63701 1.5759 6.12944 1.37734 5.59979 1.29167L5.56914 1.48115C5.54709 1.61745 5.41873 1.70908 5.28116 1.69737C5.10127 1.68206 4.92044 1.68147 4.74046 1.6956C4.60282 1.70641 4.47506 1.61395 4.4539 1.4775L4.4245 1.28782C3.88607 1.37129 3.36985 1.5713 2.91298 1.87656C2.87545 1.90164 2.83843 1.92736 2.80192 1.9537L2.91424 2.10937C2.99503 2.22134 2.96906 2.37689 2.8635 2.46589C2.72547 2.58226 2.59718 2.70971 2.47992 2.84697C2.39023 2.95194 2.23451 2.9769 2.12307 2.89538L1.96814 2.78204C1.79223 3.02251 1.64464 3.28428 1.52943 3.56243C1.41738 3.83293 1.33794 4.11413 1.29167 4.40019L1.48118 4.43084C1.61748 4.45288 1.70911 4.58125 1.6974 4.71882C1.68209 4.8987 1.6815 5.07954 1.69563 5.25951C1.70644 5.39716 1.61398 5.52492 1.47753 5.54607L1.28783 5.57548C1.29598 5.62805 1.30525 5.68051 1.31566 5.73284C1.43899 6.35284 1.71639 6.92988 2.11999 7.41182C2.23752 7.55217 2.2388 7.76117 2.10936 7.89061C1.97992 8.02005 1.76899 8.02082 1.64961 7.88204C1.15426 7.3062 0.8144 6.61081 0.665486 5.86217C0.494962 5.00489 0.582481 4.11629 0.916976 3.30875C1.25147 2.5012 1.81792 1.81098 2.54469 1.32537C3.27146 0.839761 4.12591 0.580566 4.99999 0.580566C5.87406 0.580566 6.72851 0.839761 7.45528 1.32537C8.18205 1.81098 8.7485 2.5012 9.08299 3.30875C9.41749 4.11629 9.50501 5.00489 9.33449 5.86217ZM4.31869 4.73935C4.14267 5.1143 4.30394 5.56095 4.67889 5.73697C5.05384 5.91299 5.5005 5.75172 5.67652 5.37677C5.75425 5.21118 5.7662 5.0316 5.72302 4.86752C5.71998 4.85597 5.71938 4.84389 5.72168 4.83217L6.1084 2.86309C6.12345 2.78648 6.026 2.74073 5.97667 2.80125L4.70882 4.35669C4.70128 4.36595 4.6916 4.37321 4.68077 4.37824C4.52695 4.44983 4.39643 4.57375 4.31869 4.73935Z" + fill="currentColor" /> + </g> + <defs> + <clipPath id="clip0_1663_11611"> + <rect width="10" height="10" fill="currentColor" /> + </clipPath> + </defs> +</svg> \ No newline at end of file diff --git a/control-station/src/assets/svg/restart_icon.svg b/control-station/src/assets/svg/restart_icon.svg deleted file mode 100644 index 8c6ce3bbc..000000000 --- a/control-station/src/assets/svg/restart_icon.svg +++ /dev/null @@ -1,4 +0,0 @@ -<svg width="3em" height="3em" viewBox="0 0 38 40" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M16.3468 7.71331C14.8947 8.07691 13.3885 8.63716 11.8794 9.51118C5.05988 13.4607 2.72074 21.939 6.65247 28.456C10.5924 34.9868 19.3011 37.0666 26.1206 33.117C32.9401 29.1676 35.2792 20.6894 31.3475 14.1722M11.4696 4.88324L18.639 6.38577L15.5306 13.1431" stroke="#E6F0FF" stroke-width="9" stroke-linecap="round" stroke-linejoin="round"/> -<path d="M16.3468 7.71331C14.8947 8.07691 13.3885 8.63716 11.8794 9.51118C5.05988 13.4607 2.72074 21.939 6.65247 28.456C10.5924 34.9868 19.3011 37.0666 26.1206 33.117C32.9401 29.1676 35.2792 20.6894 31.3475 14.1722M11.4696 4.88324L18.639 6.38577L15.5306 13.1431" stroke="#2D74FF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/> -</svg> diff --git a/control-station/src/assets/svg/roll-rotation.svg b/control-station/src/assets/svg/roll-rotation.svg index 3ec79e1ec..599da43d4 100644 --- a/control-station/src/assets/svg/roll-rotation.svg +++ b/control-station/src/assets/svg/roll-rotation.svg @@ -1,4 +1,7 @@ <svg width="30" height="45" viewBox="0 0 30 45" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M3.12891 7C10.7956 1.5 18.4622 1.5 26.1289 7M26.1289 7L21.2003 9M26.1289 7L24.486 2" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/> -<path d="M4.08817 23.6492L2.6399 28.7747C2.47856 29.3457 3.00727 29.2609 3.58386 29.4238L7.49458 30.5289C8.07118 30.6918 8.40781 31.2867 8.24647 31.8578L8.01276 32.6849C7.85142 33.2559 7.2532 33.5867 6.6766 33.4238L2.7659 32.3188C2.1893 32.1558 1.59108 32.4867 1.42973 33.0577L0.319616 36.9865C0.158274 37.5575 0.494907 38.1525 1.07151 38.3154L8.2426 40.3416C8.67505 40.4638 9.12372 40.2157 9.24472 39.7875C9.36573 39.3592 9.8144 39.1111 10.2468 39.2333L12.8179 39.9597C13.2503 40.0819 13.5028 40.5282 13.3818 40.9564C13.2608 41.3847 13.5133 41.8309 13.9457 41.9531L21.1168 43.9793C21.6934 44.1423 22.2916 43.8115 22.453 43.2404L23.5631 39.3116C23.7244 38.7406 23.3878 38.1457 22.8112 37.9827L18.9005 36.8777C18.3239 36.7148 17.9873 36.1198 18.1486 35.5488L18.3823 34.7217C18.5437 34.1507 19.1419 33.8199 19.7185 33.9828L23.6292 35.0878C24.2058 35.2507 24.612 35.5997 24.7733 35.0287L26.2215 29.9032C29.2598 19.1506 18.1639 16.127 18.1639 16.127C18.1639 16.127 7.12639 12.8967 4.08817 23.6492Z" fill="black"/> -</svg> + <path d="M3.12891 7C10.7956 1.5 18.4622 1.5 26.1289 7M26.1289 7L21.2003 9M26.1289 7L24.486 2" stroke="currentColor" + stroke-width="3" stroke-linecap="round" stroke-linejoin="round" /> + <path + d="M4.08817 23.6492L2.6399 28.7747C2.47856 29.3457 3.00727 29.2609 3.58386 29.4238L7.49458 30.5289C8.07118 30.6918 8.40781 31.2867 8.24647 31.8578L8.01276 32.6849C7.85142 33.2559 7.2532 33.5867 6.6766 33.4238L2.7659 32.3188C2.1893 32.1558 1.59108 32.4867 1.42973 33.0577L0.319616 36.9865C0.158274 37.5575 0.494907 38.1525 1.07151 38.3154L8.2426 40.3416C8.67505 40.4638 9.12372 40.2157 9.24472 39.7875C9.36573 39.3592 9.8144 39.1111 10.2468 39.2333L12.8179 39.9597C13.2503 40.0819 13.5028 40.5282 13.3818 40.9564C13.2608 41.3847 13.5133 41.8309 13.9457 41.9531L21.1168 43.9793C21.6934 44.1423 22.2916 43.8115 22.453 43.2404L23.5631 39.3116C23.7244 38.7406 23.3878 38.1457 22.8112 37.9827L18.9005 36.8777C18.3239 36.7148 17.9873 36.1198 18.1486 35.5488L18.3823 34.7217C18.5437 34.1507 19.1419 33.8199 19.7185 33.9828L23.6292 35.0878C24.2058 35.2507 24.612 35.5997 24.7733 35.0287L26.2215 29.9032C29.2598 19.1506 18.1639 16.127 18.1639 16.127C18.1639 16.127 7.12639 12.8967 4.08817 23.6492Z" + fill="currentColor" /> +</svg> \ No newline at end of file diff --git a/control-station/src/assets/svg/stop_icon.svg b/control-station/src/assets/svg/stop_icon.svg deleted file mode 100644 index fcbb8a33e..000000000 --- a/control-station/src/assets/svg/stop_icon.svg +++ /dev/null @@ -1,4 +0,0 @@ -<svg width="3em" height="3em" viewBox="0 0 43 42" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M28.0385 1.55194L14.8283 1.55194C13.9172 1.55194 13.0434 1.91389 12.3991 2.55817L3.05811 11.8992C2.41383 12.5434 2.05188 13.4173 2.05188 14.3284L2.05188 27.5386C2.05188 28.4497 2.41383 29.3235 3.05811 29.9678L12.3991 39.3088C13.0434 39.9531 13.9172 40.315 14.8283 40.315H28.0385C28.9496 40.315 29.8235 39.9531 30.4677 39.3088L39.8087 29.9678C40.453 29.3235 40.815 28.4497 40.815 27.5386V14.3284C40.815 13.4173 40.453 12.5434 39.8087 11.8992L30.4677 2.55817C29.8235 1.91389 28.9496 1.55194 28.0385 1.55194Z" fill="#FF2424" stroke="#FFEEEE" stroke-width="3"/> -<rect x="9.21594" y="17.2321" width="24.7742" height="7.74194" fill="white"/> -</svg> diff --git a/control-station/src/assets/svg/thermometer-field.svg b/control-station/src/assets/svg/thermometer-field.svg index 0cfa7e28f..584b61708 100644 --- a/control-station/src/assets/svg/thermometer-field.svg +++ b/control-station/src/assets/svg/thermometer-field.svg @@ -1,20 +1,4 @@ -<svg width="18" height="28" viewBox="0 0 18 28" fill="none" xmlns="http://www.w3.org/2000/svg"> -<g clip-path="url(#clip0_1663_11604)" filter="url(#filter0_d_1663_11604)"> -<path d="M9 20C11.7614 20 14 17.7614 14 15C14 12.9497 12.7659 11.1876 11 10.416V1C11 0.447715 10.5523 0 10 0H8C7.44772 0 7 0.447715 7 1V10.416C5.2341 11.1876 4 12.9497 4 15C4 17.7614 6.23858 20 9 20Z" fill="black"/> -</g> -<defs> -<filter id="filter0_d_1663_11604" x="0" y="0" width="18" height="28" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> -<feFlood flood-opacity="0" result="BackgroundImageFix"/> -<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> -<feOffset dy="4"/> -<feGaussianBlur stdDeviation="2"/> -<feComposite in2="hardAlpha" operator="out"/> -<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> -<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1663_11604"/> -<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1663_11604" result="shape"/> -</filter> -<clipPath id="clip0_1663_11604"> -<rect width="10" height="20" fill="white" transform="translate(4)"/> -</clipPath> -</defs> -</svg> +<svg width="18" height="28" viewBox="0 0 18 28" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> + <path + d="M9 20C11.7614 20 14 17.7614 14 15C14 12.9497 12.7659 11.1876 11 10.416V1C11 0.447715 10.5523 0 10 0H8C7.44772 0 7 0.447715 7 1V10.416C5.2341 11.1876 4 12.9497 4 15C4 17.7614 6.23858 20 9 20Z" /> +</svg> \ No newline at end of file diff --git a/control-station/src/assets/svg/thermometer-filled.svg b/control-station/src/assets/svg/thermometer-filled.svg index c8a3b4d7f..7aa1c8a04 100644 --- a/control-station/src/assets/svg/thermometer-filled.svg +++ b/control-station/src/assets/svg/thermometer-filled.svg @@ -1,3 +1,4 @@ -<svg width="10" height="20" viewBox="0 0 10 20" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M5 20C7.76142 20 10 17.7614 10 15C10 12.9497 8.76591 11.1876 7 10.416V1C7 0.447715 6.55228 0 6 0H4C3.44772 0 3 0.447715 3 1V10.416C1.2341 11.1876 0 12.9497 0 15C0 17.7614 2.23858 20 5 20Z" fill="black"/> -</svg> +<svg width="10" height="20" viewBox="0 0 10 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> + <path + d="M5 20C7.76142 20 10 17.7614 10 15C10 12.9497 8.76591 11.1876 7 10.416V1C7 0.447715 6.55228 0 6 0H4C3.44772 0 3 0.447715 3 1V10.416C1.2341 11.1876 0 12.9497 0 15C0 17.7614 2.23858 20 5 20Z" /> +</svg> \ No newline at end of file diff --git a/control-station/src/assets/svg/thunder-filled.svg b/control-station/src/assets/svg/thunder-filled.svg index f4e9f52f6..040b1d617 100644 --- a/control-station/src/assets/svg/thunder-filled.svg +++ b/control-station/src/assets/svg/thunder-filled.svg @@ -1,3 +1,4 @@ -<svg width="9" height="14" viewBox="0 0 9 14" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M7.75068 0.179386L4.10418 0.528383C3.73291 0.563916 3.41227 0.803108 3.27241 1.14886L0.606314 7.74016C0.327663 8.42906 0.871125 9.17045 1.61195 9.11204L1.78178 9.09865C2.45056 9.04593 2.98055 9.65368 2.83732 10.3091L2.31658 12.6917C2.08923 13.732 3.42898 14.3597 4.08281 13.5193L8.61123 7.69824C9.09688 7.07396 8.55365 6.1803 7.77588 6.32402C7.0638 6.4556 6.51764 5.70225 6.86415 5.0664L8.72404 1.65334C9.1076 0.949481 8.54861 0.103018 7.75068 0.179386Z" fill="black"/> -</svg> +<svg width="9" height="14" viewBox="0 0 9 14" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> + <path + d="M7.75068 0.179386L4.10418 0.528383C3.73291 0.563916 3.41227 0.803108 3.27241 1.14886L0.606314 7.74016C0.327663 8.42906 0.871125 9.17045 1.61195 9.11204L1.78178 9.09865C2.45056 9.04593 2.98055 9.65368 2.83732 10.3091L2.31658 12.6917C2.08923 13.732 3.42898 14.3597 4.08281 13.5193L8.61123 7.69824C9.09688 7.07396 8.55365 6.1803 7.77588 6.32402C7.0638 6.4556 6.51764 5.70225 6.86415 5.0664L8.72404 1.65334C9.1076 0.949481 8.54861 0.103018 7.75068 0.179386Z" /> +</svg> \ No newline at end of file diff --git a/control-station/src/assets/svg/vehicle-track.svg b/control-station/src/assets/svg/vehicle-track.svg index d31495565..b902d1564 100644 --- a/control-station/src/assets/svg/vehicle-track.svg +++ b/control-station/src/assets/svg/vehicle-track.svg @@ -1,11 +1,31 @@ -<svg width="196" height="629" viewBox="0 0 196 629" fill="none" xmlns="http://www.w3.org/2000/svg"> -<rect x="50.5" width="96" height="629" fill="#64849D"/> -<line x1="97" y1="1.52494e-07" x2="97" y2="629" stroke="#C4D7E5" stroke-width="7"/> -<rect x="49.5" width="28" height="629" fill="#9CBDD7"/> -<rect x="118.5" width="28" height="629" fill="#9CBDD7"/> -<rect x="49.5" y="492" width="97" height="5" fill="#64849D"/> -<path d="M36.5 3.99999C36.5 1.79085 38.2909 0 40.5 0H49.5V629H40.5C38.2909 629 36.5 627.209 36.5 625V3.99999Z" fill="#537B87"/> -<rect x="0.5" y="485" width="49" height="19" rx="4" fill="#537B87"/> -<rect x="146.5" y="485" width="49" height="19" rx="4" fill="#537B87"/> -<path d="M146.5 0H155.5C157.709 0 159.5 1.79086 159.5 4V625C159.5 627.209 157.709 629 155.5 629H146.5V0Z" fill="#537B87"/> +<svg width="167" height="629" viewBox="0 0 167 629" fill="none" xmlns="http://www.w3.org/2000/svg"> +<rect x="7" y="22" width="24" height="24" rx="2" fill="#C4D7E5"/> +<rect x="31" y="29" width="4" height="11" fill="#C4D7E5"/> +<rect x="7" y="134" width="24" height="24" rx="2" fill="#C4D7E5"/> +<rect x="31" y="141" width="4" height="11" fill="#C4D7E5"/> +<rect y="470" width="24" height="24" rx="2" fill="#C4D7E5"/> +<rect x="24" y="477" width="4" height="11" fill="#C4D7E5"/> +<rect x="7" y="582" width="24" height="24" rx="2" fill="#C4D7E5"/> +<rect x="31" y="589" width="4" height="11" fill="#C4D7E5"/> +<rect x="7" y="246" width="24" height="24" rx="2" fill="#C4D7E5"/> +<rect x="31" y="253" width="4" height="11" fill="#C4D7E5"/> +<rect x="7" y="358" width="24" height="24" rx="2" fill="#C4D7E5"/> +<rect x="31" y="365" width="4" height="11" fill="#C4D7E5"/> +<rect width="24" height="24" rx="2" transform="matrix(-1 0 0 1 160 22)" fill="#C4D7E5"/> +<rect width="4" height="11" transform="matrix(-1 0 0 1 136 29)" fill="#C4D7E5"/> +<rect width="24" height="24" rx="2" transform="matrix(-1 0 0 1 160 134)" fill="#C4D7E5"/> +<rect width="4" height="11" transform="matrix(-1 0 0 1 136 141)" fill="#C4D7E5"/> +<rect width="24" height="24" rx="2" transform="matrix(-1 0 0 1 167 470)" fill="#C4D7E5"/> +<rect width="4" height="11" transform="matrix(-1 0 0 1 143 477)" fill="#C4D7E5"/> +<rect width="24" height="24" rx="2" transform="matrix(-1 0 0 1 160 582)" fill="#C4D7E5"/> +<rect width="4" height="11" transform="matrix(-1 0 0 1 136 589)" fill="#C4D7E5"/> +<rect width="24" height="24" rx="2" transform="matrix(-1 0 0 1 160 246)" fill="#C4D7E5"/> +<rect width="4" height="11" transform="matrix(-1 0 0 1 136 253)" fill="#C4D7E5"/> +<rect width="24" height="24" rx="2" transform="matrix(-1 0 0 1 160 358)" fill="#C4D7E5"/> +<rect width="4" height="11" transform="matrix(-1 0 0 1 136 365)" fill="#C4D7E5"/> +<line x1="82.5" y1="1.52494e-07" x2="82.5" y2="629" stroke="#C4D7E5" stroke-width="7"/> +<rect x="35" y="1" width="28" height="628" rx="3" fill="#9CBDD7"/> +<rect x="104" y="1" width="28" height="628" rx="3" fill="#9CBDD7"/> +<rect x="28" y="392" width="21" height="125" rx="4" fill="#5894A7"/> +<rect x="118" y="392" width="21" height="125" rx="4" fill="#5894A7"/> </svg> diff --git a/control-station/src/assets/svg/vesper-icon.svg b/control-station/src/assets/svg/vesper-icon.svg new file mode 100644 index 000000000..e98b29a5c --- /dev/null +++ b/control-station/src/assets/svg/vesper-icon.svg @@ -0,0 +1,11 @@ +<svg width="69" height="190" viewBox="0 0 69 190" fill="none" xmlns="http://www.w3.org/2000/svg"> +<path d="M19.0851 185.851C7.8251 182.82 0 172.611 0 160.95L0 29.05C0 17.3893 7.8251 7.17958 19.0851 4.14894V4.14894C29.182 1.43133 39.818 1.43133 49.9149 4.14894V4.14894C61.1749 7.17958 69 17.3893 69 29.05L69 160.95C69 172.611 61.1749 182.82 49.9149 185.851V185.851C39.818 188.569 29.182 188.569 19.0851 185.851V185.851Z" fill="#FFD1B0"/> +<path d="M63.2016 138.1L16.99 125.72C16.9594 125.711 16.928 125.705 16.8962 125.702C11.0255 125.03 8.0085 130.296 5.43631 136.478C5.39513 136.577 5.38377 136.686 5.40361 136.791C5.42346 136.896 5.47365 136.993 5.54799 137.07C5.62233 137.147 5.71757 137.2 5.82195 137.224C5.92632 137.247 6.03527 137.239 6.13534 137.201C12.9925 134.478 23.1118 134.632 40.1033 140.061C40.1944 140.091 40.2762 140.144 40.3406 140.215C40.405 140.285 40.4498 140.372 40.4706 140.465C40.4913 140.559 40.4874 140.656 40.4591 140.748C40.4307 140.839 40.3791 140.922 40.3091 140.987C34.6927 146.262 27.2152 149.46 17.1019 149.733C16.9946 149.734 16.8898 149.766 16.7993 149.823C11.8759 153.085 10.1994 158.499 9.16749 164.352C9.14708 164.461 9.1602 164.573 9.20511 164.674C9.25003 164.776 9.32463 164.861 9.41906 164.919C9.51349 164.977 9.6233 165.005 9.73399 165C9.84468 164.994 9.95104 164.955 10.039 164.887C20.2158 156.965 30.447 153.24 40.5179 156.045C40.6438 156.082 40.7787 156.071 40.8973 156.015C41.016 155.96 41.1102 155.863 41.1625 155.742C44.6304 147.923 52.4831 142.685 63.2258 139.15C63.3382 139.114 63.436 139.043 63.5044 138.946C63.5729 138.85 63.6084 138.734 63.6057 138.616C63.603 138.498 63.5622 138.384 63.4893 138.291C63.4165 138.198 63.3156 138.131 63.2016 138.1Z" fill="#C85B0F"/> +<path d="M17.6288 84.2602L24.5495 86.1183C24.6657 86.1501 24.7682 86.219 24.8415 86.3147C24.9147 86.4103 24.9546 86.5274 24.955 86.6478V89.5711C24.955 89.6559 24.9353 89.7396 24.8974 89.8156C24.8596 89.8915 24.8046 89.9576 24.7368 90.0087C24.669 90.0597 24.5903 90.0944 24.5068 90.1098C24.4234 90.1252 24.3375 90.121 24.2559 90.0976L21.6383 89.338C21.5574 89.3163 21.4725 89.3134 21.3902 89.3296C21.3079 89.3458 21.2305 89.3807 21.1638 89.4316C21.0972 89.4824 21.0431 89.5479 21.0057 89.623C20.9684 89.6981 20.9488 89.7807 20.9484 89.8646V92.3188C20.9476 92.5593 21.0264 92.7933 21.1726 92.9844C21.3187 93.1754 21.524 93.3127 21.7564 93.3749L36.2817 97.2695C36.3625 97.2913 36.4472 97.2943 36.5294 97.278C36.6115 97.2618 36.6888 97.2269 36.7553 97.176C36.8217 97.125 36.8755 97.0595 36.9125 96.9844C36.9495 96.9093 36.9687 96.8266 36.9686 96.7429L36.9686 90.6907C36.9684 90.524 37.0064 90.3594 37.0797 90.2096C37.1529 90.0598 37.2595 89.9287 37.3912 89.8265C37.523 89.7242 37.6764 89.6535 37.8397 89.6197C38.003 89.586 38.1719 89.59 38.3333 89.6316L46.525 91.7499C47.228 91.9316 47.8508 92.3416 48.2956 92.9156C48.7403 93.4896 48.9818 94.1951 48.9822 94.9212L48.9822 107.97C48.9817 108.053 48.9621 108.136 48.925 108.21C48.8878 108.285 48.834 108.35 48.7677 108.401C48.7014 108.452 48.6244 108.487 48.5425 108.504C48.4606 108.52 48.3761 108.518 48.2953 108.496L45.3811 107.716C45.2655 107.684 45.1634 107.616 45.0906 107.521C45.0178 107.425 44.9785 107.309 44.9787 107.189V100.855C44.9791 100.495 44.8608 100.145 44.6419 99.8587C44.4231 99.5726 44.116 99.3666 43.7682 99.2727L41.6651 98.7129C41.5843 98.6911 41.4995 98.6881 41.4174 98.7043C41.3352 98.7205 41.2579 98.7555 41.1915 98.8064C41.125 98.8573 41.0712 98.9229 41.0342 98.998C40.9972 99.0731 40.978 99.1557 40.9782 99.2395V105.119C40.9782 105.287 40.9398 105.452 40.8658 105.602C40.7919 105.752 40.6843 105.884 40.5515 105.986C40.4187 106.088 40.2642 106.158 40.1 106.191C39.9358 106.224 39.7662 106.218 39.6043 106.175L19.3779 100.756C18.6826 100.567 18.0687 100.155 17.6311 99.5831C17.1935 99.0109 16.9566 98.3105 16.957 97.5902L16.957 84.7898C16.957 84.7075 16.9757 84.6262 17.0116 84.5521C17.0475 84.4781 17.0998 84.4131 17.1645 84.3621C17.2291 84.3112 17.3045 84.2755 17.3849 84.2579C17.4653 84.2402 17.5487 84.241 17.6288 84.2602Z" fill="#C85B0F"/> +<path d="M48.9851 128.036L48.9851 113.141C48.9851 113.021 48.9453 112.904 48.872 112.809C48.7986 112.714 48.6958 112.646 48.5796 112.615L45.6685 111.834C45.5877 111.812 45.5029 111.809 45.4208 111.826C45.3387 111.842 45.2614 111.877 45.1949 111.928C45.1285 111.979 45.0747 112.044 45.0377 112.119C45.0007 112.194 44.9815 112.277 44.9816 112.361L44.9816 118.955C44.9812 119.038 44.9616 119.121 44.9243 119.196C44.8869 119.271 44.8328 119.337 44.7662 119.388C44.6995 119.438 44.6221 119.473 44.5398 119.49C44.4575 119.506 44.3726 119.503 44.2916 119.481L41.3805 118.7C41.2643 118.67 41.1615 118.601 41.0882 118.506C41.0148 118.411 40.9751 118.294 40.975 118.174L40.975 111.002C40.9747 110.881 40.9348 110.764 40.8616 110.669C40.7883 110.573 40.6857 110.504 40.5695 110.472L37.6584 109.695C37.5775 109.673 37.4926 109.67 37.4103 109.686C37.328 109.702 37.2506 109.737 37.1839 109.788C37.1173 109.839 37.0632 109.905 37.0258 109.98C36.9885 110.055 36.9688 110.137 36.9685 110.221L36.9685 116.806C36.9691 116.89 36.9502 116.973 36.9134 117.048C36.8766 117.124 36.8229 117.19 36.7564 117.241C36.6899 117.292 36.6124 117.328 36.53 117.344C36.4477 117.36 36.3626 117.358 36.2816 117.336L21.7593 113.444C21.5266 113.382 21.3209 113.245 21.1743 113.054C21.0276 112.863 20.9482 112.629 20.9483 112.388V105.637C20.9486 105.516 20.9094 105.4 20.8367 105.304C20.7639 105.208 20.6617 105.139 20.5458 105.107L17.6317 104.326C17.5507 104.305 17.4658 104.303 17.3837 104.319C17.3016 104.336 17.2244 104.371 17.1581 104.422C17.0917 104.473 17.038 104.539 17.001 104.614C16.964 104.69 16.9448 104.772 16.9448 104.856L16.9448 117.65C16.9443 118.371 17.1813 119.071 17.6188 119.643C18.0564 120.215 18.6704 120.627 19.3656 120.816L48.2982 128.575C48.38 128.596 48.4656 128.598 48.5484 128.581C48.6312 128.564 48.7089 128.528 48.7755 128.476C48.842 128.424 48.8955 128.357 48.9319 128.28C48.9683 128.204 48.9865 128.12 48.9851 128.036Z" fill="#C85B0F"/> +<path d="M48.9851 68.6144L48.9851 53.0118C48.9848 52.8921 48.9452 52.7758 48.8725 52.6808C48.7999 52.5857 48.698 52.517 48.5826 52.4853L45.6685 51.7045C45.5875 51.6832 45.5026 51.6808 45.4205 51.6975C45.3384 51.7141 45.2612 51.7493 45.1949 51.8005C45.1285 51.8516 45.0748 51.9173 45.0378 51.9925C45.0008 52.0677 44.9816 52.1503 44.9816 52.2341L44.9816 58.8189C44.9815 58.9027 44.9623 58.9853 44.9253 59.0605C44.8883 59.1357 44.8346 59.2014 44.7683 59.2525C44.7019 59.3037 44.6247 59.3389 44.5426 59.3555C44.4605 59.3722 44.3757 59.3698 44.2946 59.3485L41.3805 58.5677C41.2643 58.536 41.1617 58.467 41.0885 58.3713C41.0152 58.2757 40.9754 58.1586 40.975 58.0382L40.975 50.8663C40.9745 50.7462 40.9345 50.6296 40.8612 50.5345C40.788 50.4394 40.6855 50.3709 40.5695 50.3398L37.6584 49.559C37.5776 49.5372 37.4928 49.5342 37.4107 49.5504C37.3285 49.5667 37.2513 49.6016 37.1848 49.6525C37.1184 49.7035 37.0645 49.769 37.0276 49.8441C36.9906 49.9192 36.9714 50.0018 36.9715 50.0856L36.9715 56.6734C36.9711 56.7572 36.9515 56.8399 36.9141 56.915C36.8768 56.99 36.8227 57.0555 36.7561 57.1064C36.6894 57.1573 36.6119 57.1922 36.5297 57.2084C36.4474 57.2246 36.3625 57.2217 36.2815 57.1999L21.7562 53.3084C21.5244 53.2464 21.3195 53.1095 21.1734 52.9191C21.0273 52.7287 20.9481 52.4953 20.9483 52.2553V45.501C20.9483 45.3808 20.9085 45.264 20.8351 45.1687C20.7618 45.0735 20.659 45.0052 20.5428 44.9745L17.6286 44.1937C17.5478 44.1719 17.4631 44.1689 17.3809 44.1852C17.2988 44.2014 17.2215 44.2363 17.155 44.2872C17.0886 44.3382 17.0348 44.4037 16.9978 44.4788C16.9608 44.5539 16.9416 44.6366 16.9417 44.7203L16.9417 57.5146C16.9413 58.235 17.1782 58.9354 17.6158 59.5075C18.0534 60.0797 18.6673 60.4917 19.3626 60.68L48.9851 68.6144Z" fill="#C85B0F"/> +<path d="M14.8749 112.117C14.2696 115.216 12.2896 117.886 8.93463 120.127V116.275C8.93447 114.995 9.24868 113.734 9.84967 112.604C10.4507 111.474 11.32 110.508 12.3814 109.793L14.8749 108.113V112.117Z" fill="#C85B0F"/> +<path d="M16.9448 28.277L16.9448 39.6067C16.9448 39.7269 16.9846 39.8438 17.0579 39.939C17.1313 40.0343 17.234 40.1025 17.3503 40.1333L48.2982 48.4278C48.379 48.449 48.4635 48.4515 48.5454 48.435C48.6273 48.4186 48.7043 48.3836 48.7706 48.3328C48.8369 48.282 48.8907 48.2167 48.9279 48.1419C48.965 48.0671 48.9846 47.9848 48.9851 47.9013V41.0199C48.9846 40.8998 48.9446 40.7833 48.8713 40.6881C48.7981 40.593 48.6956 40.5246 48.5796 40.4934L41.3805 38.5627C41.2643 38.532 41.1615 38.4637 41.0882 38.3685C41.0148 38.2732 40.975 38.1564 40.975 38.0362V35.8725C40.9748 35.7051 41.0132 35.5398 41.0872 35.3896C41.1612 35.2394 41.2688 35.1083 41.4017 35.0064C41.5346 34.9046 41.6892 34.8347 41.8534 34.8023C42.0177 34.7698 42.1872 34.7757 42.3489 34.8194L48.2982 36.4112C48.379 36.433 48.4638 36.436 48.5459 36.4197C48.628 36.4035 48.7053 36.3686 48.7718 36.3177C48.8382 36.2667 48.892 36.2012 48.929 36.1261C48.966 36.051 48.9852 35.9684 48.9851 35.8846V33.0098C48.9851 32.8896 48.9453 32.7728 48.872 32.6776C48.7986 32.5823 48.6958 32.514 48.5796 32.4833L42.246 30.7856C41.8494 30.6788 41.4338 30.6631 41.0303 30.7396C40.6268 30.8161 40.2458 30.9829 39.9159 31.2275L39.0383 31.875L37.6372 30.1804C37.2037 29.6541 36.6165 29.2764 35.9577 29.1001L21.0693 25.1117C20.5839 24.9818 20.075 24.9652 19.5821 25.0634C19.0892 25.1615 18.6255 25.3717 18.2268 25.6776C17.8281 25.9836 17.5051 26.3772 17.2828 26.8279C17.0605 27.2786 16.9448 27.7744 16.9448 28.277ZM22.3342 29.4511L34.5506 32.7254C35.2459 32.9136 35.8598 33.3257 36.2974 33.8978C36.735 34.47 36.9719 35.1704 36.9715 35.8907V36.6714C36.9716 36.7551 36.9524 36.8378 36.9154 36.9129C36.8784 36.988 36.8246 37.0535 36.7582 37.1045C36.6917 37.1554 36.6144 37.1903 36.5323 37.2065C36.4502 37.2228 36.3654 37.2198 36.2846 37.198L21.3568 33.1975C21.2409 33.1663 21.1384 33.0978 21.0651 33.0027C20.9918 32.9076 20.9518 32.791 20.9513 32.6709V30.5072C20.9515 30.3389 20.9904 30.173 21.0651 30.0221C21.1397 29.8713 21.2482 29.7397 21.3819 29.6376C21.5157 29.5354 21.6711 29.4655 21.8363 29.4331C22.0015 29.4008 22.1719 29.407 22.3342 29.4511Z" fill="#C85B0F"/> +<path d="M16.9448 68.3209L16.9448 79.6567C16.9446 79.7773 16.9843 79.8946 17.0576 79.9903C17.1309 80.0861 17.2338 80.155 17.3503 80.1863L48.2982 88.4778C48.3792 88.4996 48.4641 88.5025 48.5463 88.4862C48.6286 88.47 48.7061 88.4351 48.7727 88.3843C48.8394 88.3334 48.8935 88.2679 48.9308 88.1928C48.9682 88.1178 48.9878 88.0351 48.9882 87.9513V81.0427C48.9883 80.9221 48.9486 80.8048 48.8753 80.709C48.802 80.6133 48.6991 80.5444 48.5827 80.5131L41.3805 78.6127C41.2643 78.582 41.1615 78.5137 41.0882 78.4184C41.0148 78.3232 40.9751 78.2064 40.975 78.0862L40.975 72.9811C40.9755 72.2608 40.7386 71.5604 40.301 70.9883C39.8634 70.4161 39.2494 70.004 38.5542 69.8158L21.0754 65.1556C20.5895 65.0246 20.08 65.0072 19.5863 65.1048C19.0926 65.2024 18.6281 65.4124 18.2286 65.7185C17.8292 66.0246 17.5056 66.4186 17.283 66.8699C17.0603 67.3212 16.9446 67.8177 16.9448 68.3209ZM22.3343 69.5041L36.1605 73.196C36.3929 73.2581 36.5982 73.3954 36.7443 73.5865C36.8905 73.7775 36.9693 74.0115 36.9685 74.2521L36.9685 76.7063C36.9686 76.79 36.9494 76.8726 36.9124 76.9477C36.8754 77.0228 36.8216 77.0884 36.7552 77.1393C36.6887 77.1902 36.6114 77.2252 36.5293 77.2414C36.4471 77.2576 36.3624 77.2547 36.2816 77.2328L21.3538 73.2353C21.2376 73.2035 21.135 73.1346 21.0618 73.0389C20.9885 72.9433 20.9487 72.8262 20.9483 72.7058V70.5451C20.95 70.3776 20.9903 70.2126 21.0659 70.0631C21.1416 69.9136 21.2506 69.7835 21.3846 69.6829C21.5186 69.5823 21.6739 69.5138 21.8386 69.4828C22.0033 69.4518 22.1729 69.4591 22.3343 69.5041Z" fill="#C85B0F"/> +<path d="M54.1659 130.28L57.6308 131.209C57.7116 131.231 57.7964 131.233 57.8785 131.217C57.9607 131.201 58.038 131.166 58.1044 131.115C58.1709 131.064 58.2247 130.999 58.2617 130.924C58.2987 130.848 58.3179 130.766 58.3177 130.682L58.3177 35.6897C58.3179 35.5691 58.2782 35.4519 58.2049 35.3561C58.1316 35.2604 58.0287 35.1915 57.9122 35.1602L54.4474 34.2342C54.3665 34.2123 54.2818 34.2094 54.1996 34.2256C54.1175 34.2418 54.0402 34.2767 53.9738 34.3277C53.9073 34.3786 53.8535 34.4442 53.8165 34.5193C53.7795 34.5944 53.7603 34.677 53.7604 34.7607L53.7604 129.753C53.7605 129.873 53.8002 129.99 53.8736 130.085C53.9469 130.181 54.0497 130.249 54.1659 130.28Z" fill="#C85B0F"/> +</svg> diff --git a/control-station/src/assets/svg/y-index.svg b/control-station/src/assets/svg/y-index.svg index 87dad8b2d..84f53c007 100644 --- a/control-station/src/assets/svg/y-index.svg +++ b/control-station/src/assets/svg/y-index.svg @@ -1,3 +1,4 @@ <svg width="15" height="13" viewBox="0 0 15 13" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M1 6.5L14 6.5M1 6.5L4.46667 12M1 6.5L4.46667 1M14 6.5L10.5333 1M14 6.5L10.5333 12" stroke="#0F540E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> -</svg> + <path d="M1 6.5L14 6.5M1 6.5L4.46667 12M1 6.5L4.46667 1M14 6.5L10.5333 1M14 6.5L10.5333 12" stroke="currentColor" + stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> +</svg> \ No newline at end of file diff --git a/control-station/src/assets/svg/yaw-rotation.svg b/control-station/src/assets/svg/yaw-rotation.svg index 1dd467372..06ed6461d 100644 --- a/control-station/src/assets/svg/yaw-rotation.svg +++ b/control-station/src/assets/svg/yaw-rotation.svg @@ -1,4 +1,8 @@ <svg width="68" height="27" viewBox="0 0 68 27" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M7.02539 25.4045C1.52539 17.7379 1.52539 10.0712 7.02539 2.40454M7.02539 2.40454L9.02539 7.33311M7.02539 2.40454L2.02539 4.0474" stroke="black" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/> -<path d="M16.464 5.4097C17.4662 3.82004 19.3434 3.01109 21.1871 3.37432L61.65 11.3458C63.4938 11.709 64.9238 13.1695 65.2482 15.0205C65.6959 17.5753 65.1778 20.2053 63.7945 22.3995C62.7923 23.9891 60.9151 24.7981 59.0714 24.4348L18.6085 16.4634C16.7647 16.1002 15.3347 14.6397 15.0103 12.7887C14.5626 10.2338 15.0808 7.60382 16.464 5.4097Z" fill="black"/> -</svg> + <path + d="M7.02539 25.4045C1.52539 17.7379 1.52539 10.0712 7.02539 2.40454M7.02539 2.40454L9.02539 7.33311M7.02539 2.40454L2.02539 4.0474" + stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" /> + <path + d="M16.464 5.4097C17.4662 3.82004 19.3434 3.01109 21.1871 3.37432L61.65 11.3458C63.4938 11.709 64.9238 13.1695 65.2482 15.0205C65.6959 17.5753 65.1778 20.2053 63.7945 22.3995C62.7923 23.9891 60.9151 24.7981 59.0714 24.4348L18.6085 16.4634C16.7647 16.1002 15.3347 14.6397 15.0103 12.7887C14.5626 10.2338 15.0808 7.60382 16.464 5.4097Z" + fill="currentColor" /> +</svg> \ No newline at end of file diff --git a/control-station/src/assets/svg/z-index.svg b/control-station/src/assets/svg/z-index.svg index 03e5f91d7..316ee144c 100644 --- a/control-station/src/assets/svg/z-index.svg +++ b/control-station/src/assets/svg/z-index.svg @@ -1,3 +1,4 @@ <svg width="10" height="19" viewBox="0 0 10 19" fill="none" xmlns="http://www.w3.org/2000/svg"> -<path d="M5 1.5L5 17.5M5 1.5L1 5.76667M5 1.5L9 5.76667M5 17.5L9 13.2333M5 17.5L1 13.2333" stroke="#0F540E" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> -</svg> + <path d="M5 1.5L5 17.5M5 1.5L1 5.76667M5 1.5L9 5.76667M5 17.5L9 13.2333M5 17.5L1 13.2333" stroke="currentColor" + stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /> +</svg> \ No newline at end of file diff --git a/control-station/src/components/3dScenes/OneDofScene.tsx b/control-station/src/components/3dScenes/OneDofScene.tsx deleted file mode 100644 index 0d2669ce4..000000000 --- a/control-station/src/components/3dScenes/OneDofScene.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { Grid, OrbitControls, PerspectiveCamera } from "@react-three/drei"; -import { Canvas } from "@react-three/fiber"; -import { Vehicle } from "./Vehicle/Vehicle"; - -type Props = { - y: number; - rotX: number; - rotY: number; - rotZ: number; -}; - -export function OneDofScene({ y, rotX, rotY, rotZ }: Props) { - return ( - <Canvas style={{ flex: "1 1 0", width: "auto" }}> - <PerspectiveCamera - makeDefault - position={[7, 5, 6]} - fov={60} - /> - <OrbitControls /> - <ambientLight intensity={0.1} /> - <directionalLight - color="white" - position={[15, 20, 10]} - intensity={0.8} - /> - <Grid - args={[30, 30]} - sectionColor="#ee7623" - fadeDistance={20} - infiniteGrid - /> - <Vehicle - y={y} - rotX={rotX} - rotY={rotY} - rotZ={rotZ} - /> - </Canvas> - ); -} diff --git a/control-station/src/components/3dScenes/Vehicle/Vehicle.tsx b/control-station/src/components/3dScenes/Vehicle/Vehicle.tsx deleted file mode 100644 index 6135af78b..000000000 --- a/control-station/src/components/3dScenes/Vehicle/Vehicle.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { animated, useSpring } from "@react-spring/three"; -import { useGLTF } from "@react-three/drei"; -import { useEffect, useRef } from "react"; -import { Mesh } from "three"; - -type Props = { - y: number; - rotX: number; - rotY: number; - rotZ: number; -}; - -export function Vehicle({ y, rotX, rotY, rotZ }: Props) { - const meshRef = useRef<Mesh>(null!); - const valueRef = useRef({ y, rotX, rotY, rotZ }); - - const model = useGLTF("./pod_simplified.glb"); - - const [springs, setSprings] = useSpring(() => ({ - y: y, - rotX: rotX, - rotY: rotY, - rotZ: rotZ, - config: { - mass: 3, - tension: 15, - friction: 10, - precision: 0.00001, - }, - })); - - useEffect(() => { - valueRef.current = { y, rotX, rotY, rotZ }; - setSprings({ y, rotX, rotY, rotZ }); - }, [y, rotX, rotY, rotZ]); - - return ( - <animated.mesh - scale={3} - ref={meshRef} - position-y={springs.y} - rotation-x={springs.rotX} - rotation-y={springs.rotY} - rotation-z={springs.rotZ} - > - <primitive object={model.scene} /> - </animated.mesh> - ); -} diff --git a/control-station/src/components/3dScenes/Vehicle/pod_simplified.glb b/control-station/src/components/3dScenes/Vehicle/pod_simplified.glb deleted file mode 100644 index 13522147e..000000000 Binary files a/control-station/src/components/3dScenes/Vehicle/pod_simplified.glb and /dev/null differ diff --git a/control-station/src/components/BarIndicator/BarIndicator.module.scss b/control-station/src/components/BarIndicator/BarIndicator.module.scss index 4006c8733..cb5808595 100644 --- a/control-station/src/components/BarIndicator/BarIndicator.module.scss +++ b/control-station/src/components/BarIndicator/BarIndicator.module.scss @@ -1,6 +1,6 @@ -@use "src/styles/fonts"; +@use 'src/styles/fonts'; -.container { +.bar_indicator { position: relative; min-height: 1.8rem; width: 100%; @@ -12,11 +12,18 @@ position: absolute; width: 100%; height: 100%; + + .range_bar { + box-shadow: 2px 0 1rem 1rem currentColor; + background-color: currentColor; + height: 100%; + transition: width 0.1s ease-in-out; + } } .infoContainer { position: absolute; - padding: 0rem .8rem; + padding: 0rem 0.8rem; width: 100%; height: 100%; display: flex; @@ -28,7 +35,7 @@ position: absolute; top: -25%; left: -2%; - filter: blur(.5rem); + filter: blur(0.5rem); background-color: red; width: 40%; height: 150%; @@ -36,7 +43,7 @@ } .title { - opacity: .8; + opacity: 0.8; font-size: map-get($map: fonts.$font-sizes, $key: x-small); font-style: italic; font-weight: 300; @@ -44,27 +51,27 @@ .value { flex: 1; - opacity: .7; + opacity: 0.7; font-size: map-get($map: fonts.$font-sizes, $key: small); } .unit { flex: 1; - opacity: .7; - font-size: .8rem; + opacity: 0.7; + font-size: 0.8rem; } .icon { display: flex; justify-content: center; - opacity: .6; - width: .7rem; + opacity: 0.6; + max-width: 0.7rem; } .iconName { display: flex; flex: 1; - gap: .5rem; + gap: 0.5rem; align-items: center; height: 100%; } @@ -72,7 +79,7 @@ .valueUnits { display: flex; width: 3rem; - gap: .5rem; + gap: 0.5rem; align-items: center; justify-content: end; -} \ No newline at end of file +} diff --git a/control-station/src/components/BarIndicator/BarIndicator.tsx b/control-station/src/components/BarIndicator/BarIndicator.tsx index e0e743335..843b27bc2 100644 --- a/control-station/src/components/BarIndicator/BarIndicator.tsx +++ b/control-station/src/components/BarIndicator/BarIndicator.tsx @@ -1,68 +1,107 @@ -import { useGlobalTicker } from "common"; -import styles from "./BarIndicator.module.scss"; +import { useGlobalTicker } from 'common'; +import styles from './BarIndicator.module.scss'; import { getPercentageFromRange, getStateFromRange, State, stateToColor, stateToColorBackground, -} from "state"; -import { memo, useEffect, useRef, useState } from "react"; +} from 'state'; +import { memo, useEffect, useRef, useState } from 'react'; interface Props { icon?: string; title: string; getValue: () => number; safeRangeMin: number; + warningRangeMin: number; safeRangeMax: number; + warningRangeMax: number; units?: string; + color?: string; + backgroundColor?: string; } -export const BarIndicator = memo(({ icon, title, getValue, safeRangeMin, safeRangeMax, units }: Props) => { - const [valueState, setValueState] = useState<number>(0); - const percentage = useRef<number>(0); - const state = useRef<State>(getStateFromRange(valueState, safeRangeMin, safeRangeMax)); +export const BarIndicator = memo( + ({ + icon, + title, + getValue, + safeRangeMin, + warningRangeMin, + safeRangeMax, + warningRangeMax, + units, + color, + backgroundColor, + }: Props) => { + const [valueState, setValueState] = useState<number>(0); + const percentage = useRef<number>(0); + const state = useRef<State>( + getStateFromRange( + valueState, + safeRangeMin, + safeRangeMax, + warningRangeMin, + warningRangeMax + ) + ); - useGlobalTicker(() => { - setValueState(getValue()); - }) + useGlobalTicker(() => { + setValueState(getValue()); + }); - useEffect(() => { - percentage.current = getPercentageFromRange( - valueState, - safeRangeMin, - safeRangeMax - ) - state.current = (getStateFromRange(valueState, safeRangeMin, safeRangeMax)); - }) + useEffect(() => { + percentage.current = getPercentageFromRange( + valueState, + warningRangeMin, + warningRangeMax + ); + state.current = getStateFromRange( + valueState, + safeRangeMin, + safeRangeMax, + warningRangeMin, + warningRangeMax + ); + }); - return ( - <div className={styles.container}> - <div - className={styles.background} - style={{ backgroundColor: stateToColorBackground[state.current] }} - ></div> - - <div - className={styles.bar} - style={{ - width: percentage.current + "%", - backgroundColor: stateToColor[state.current], - }} - ></div> + return ( + <div className={styles.bar_indicator}> + <div + className={styles.background} + style={{ + backgroundColor: + backgroundColor != undefined + ? backgroundColor + : stateToColorBackground[state.current], + }} + > + <div + className={styles.range_bar} + style={{ + width: percentage.current + '%', + color: + color != undefined + ? color + : stateToColor[state.current], + }} + /> + </div> - <div className={styles.infoContainer}> - <div className={styles.iconName}> - <div className={styles.icon}> - <img src={icon} alt="" /> + <div className={styles.infoContainer}> + <div className={styles.iconName}> + <img className={styles.icon} src={icon} alt="" /> + <div className={styles.title}>{title}</div> </div> - <div className={styles.title}>{title}</div> - </div> <div className={styles.valueUnits}> - <div className={styles.value}>{valueState?.toFixed(1)}</div> + <div className={styles.value}> + {valueState?.toFixed(2)} + </div> <div className={styles.unit}>{units}</div> </div> + </div> </div> - </div> - ); -}); + ); + } +); diff --git a/control-station/src/components/BatteryConnector/BatteryConnector.module.scss b/control-station/src/components/BatteryConnector/BatteryConnector.module.scss deleted file mode 100644 index d509309a4..000000000 --- a/control-station/src/components/BatteryConnector/BatteryConnector.module.scss +++ /dev/null @@ -1,19 +0,0 @@ -.background { - width: 1rem; - height: 2.5rem; - background-color: hsla(205, 39%, 83%, 1); - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - gap: .3rem; - border-top-left-radius: 1rem; - border-top-right-radius: 1rem; -} - -.point { - background-color: hsla(194, 40%, 39%, 1); - width: .5rem; - height: .5rem; - border-radius: 100%; -} \ No newline at end of file diff --git a/control-station/src/components/BatteryConnector/BatteryConnector.tsx b/control-station/src/components/BatteryConnector/BatteryConnector.tsx deleted file mode 100644 index 9750a5c22..000000000 --- a/control-station/src/components/BatteryConnector/BatteryConnector.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import styles from "./BatteryConnector.module.scss" - -interface Props { - rotate?: boolean -} - -export const BatteryConnector = ({ rotate }: Props) => { - - return ( - <div - className={styles.background} - style={{ transform: rotate ? "rotate(180deg)" : "" }} - > - <div className={styles.point}></div> - <div className={styles.point}></div> - </div> - ) -} diff --git a/control-station/src/components/BatteryPack/BatteryPack.module.scss b/control-station/src/components/BatteryPack/BatteryPack.module.scss deleted file mode 100644 index b562d53b8..000000000 --- a/control-station/src/components/BatteryPack/BatteryPack.module.scss +++ /dev/null @@ -1,19 +0,0 @@ -.container { - display: flex; - min-width: 12rem; - max-width: 100%; - flex-direction: column; - align-items: center; - justify-content: center; - border-radius: 1rem; - overflow: hidden; - margin: .1rem 0; - - > div { - border-bottom: 1.5px solid hsla(0, 0%, 0%, 0.792); - } - - > div:last-child, > div:nth-last-child(2):nth-child(odd) { - border-bottom: none; - } -} \ No newline at end of file diff --git a/control-station/src/components/BatteryPack/BatteryPack.tsx b/control-station/src/components/BatteryPack/BatteryPack.tsx deleted file mode 100644 index f35a11333..000000000 --- a/control-station/src/components/BatteryPack/BatteryPack.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { BarIndicator } from 'components/BarIndicator/BarIndicator' -import batteryIcon from "assets/svg/battery-filled.svg" -import thermometerIcon from "assets/svg/thermometer-filled.svg" -import thunderIcon from "assets/svg/thunder-filled.svg" -import { Measurement } from 'common' -import styles from "./BatteryPack.module.scss" - -interface Props { - stateOfChargeMeasurement: Measurement; - temperatureMeasurement: Measurement; - maxCellMeasurement: Measurement; - minCellMeasurement: Measurement; - voltageMeasurement: Measurement; -} - -export const BatteryPack = ( - { - stateOfChargeMeasurement, - temperatureMeasurement, - maxCellMeasurement, - minCellMeasurement, - voltageMeasurement - }: Props -) => { - - return ( - <div className={styles.container}> - <BarIndicator - icon={batteryIcon} - title="State of Charge" - measurement={stateOfChargeMeasurement} - units="%" - /> - <BarIndicator - icon={thermometerIcon} - title="Temperature" - measurement={temperatureMeasurement} - units="ºC" - /> - <BarIndicator - icon={thunderIcon} - title="Max Cell" - measurement={maxCellMeasurement} - units="V" - /> - <BarIndicator - icon={thunderIcon} - title="Min Cell" - measurement={minCellMeasurement} - units="V" - /> - <BarIndicator - icon={thunderIcon} - title="Voltage" - measurement={voltageMeasurement} - units="V" - /> - </div> - ) -} diff --git a/control-station/src/components/BrakeVisualizer/BrakeVisualizer.module.scss b/control-station/src/components/BrakeVisualizer/BrakeVisualizer.module.scss index 56a5fae45..c0342303a 100644 --- a/control-station/src/components/BrakeVisualizer/BrakeVisualizer.module.scss +++ b/control-station/src/components/BrakeVisualizer/BrakeVisualizer.module.scss @@ -2,7 +2,7 @@ width: 100%; > img { - width: 100%; - height: 100%; + height: 212px; + width: 139px; } -} \ No newline at end of file +} diff --git a/control-station/src/components/BrakeVisualizer/BrakeVisualizer.tsx b/control-station/src/components/BrakeVisualizer/BrakeVisualizer.tsx index f52529465..856edfc05 100644 --- a/control-station/src/components/BrakeVisualizer/BrakeVisualizer.tsx +++ b/control-station/src/components/BrakeVisualizer/BrakeVisualizer.tsx @@ -1,33 +1,34 @@ -import styles from "./BrakeVisualizer.module.scss" -import brakeContracted from "assets/svg/brake-contracted.svg" -import brakeExtended from "assets/svg/brake-extended.svg" -import { useGlobalTicker } from "common" -import { useState } from "react" +import styles from './BrakeVisualizer.module.scss'; +import brakeContracted from 'assets/svg/brake-contracted.svg'; +import brakeExtended from 'assets/svg/brake-extended.svg'; +import { useGlobalTicker } from 'common'; +import { useState } from 'react'; interface Props { - getStatus: () => boolean, - rotation: "left" | "right" + getStatus: () => string; + rotation: 'left' | 'right'; } -export const BrakeVisualizer = ({ - getStatus, - rotation -}: Props) => { - +export const BrakeVisualizer = ({ getStatus, rotation }: Props) => { const [status, setStatus] = useState(getStatus()); useGlobalTicker(() => { setStatus(getStatus()); - }) + }); return ( <div className={styles.brakeVisualizerWrapper} style={{ - transform: rotation === "left" ? "rotate(0deg)" : "rotate(180deg)" + transform: rotation === 'left' ? 'scaleX(1)' : 'scaleX(-1)', }} > - <img src={status ? brakeExtended : brakeContracted} alt="Break Visualizer" /> - </div> - ) -} + <img + src={status == 'EXTENDED' ? brakeExtended : brakeContracted} + alt={ + status == 'EXTENDED' ? 'Brake Extended' : 'Brake Contracted' + } + /> + </div> + ); +}; diff --git a/control-station/src/components/Button/Button.module.scss b/control-station/src/components/Button/Button.module.scss deleted file mode 100644 index 1f0f26a90..000000000 --- a/control-station/src/components/Button/Button.module.scss +++ /dev/null @@ -1,22 +0,0 @@ -@use "src/styles/colors"; - -.buttonWrapper { - width: 100%; - height: 100%; - padding: 0.4rem; - - display: flex; - justify-content: center; - border: 1px solid colors.getColor("primary", 50); - background-color: colors.getColor("primary", 90); - color: colors.getColor("primary", 50); - border-radius: 0.4rem; - - cursor: pointer; -} - -.buttonWrapper:hover { - background-color: colors.getColor("primary", 95); - color: colors.getColor("primary", 60); - border: 1px solid colors.getColor("primary", 60); -} diff --git a/control-station/src/components/Button/Button.tsx b/control-station/src/components/Button/Button.tsx deleted file mode 100644 index 6f8ddb643..000000000 --- a/control-station/src/components/Button/Button.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import styles from "./Button.module.scss"; - -type Props = { - label: string; - onClick: (ev: React.MouseEvent) => void; -}; - -export const Button = ({ label, onClick }: Props) => { - return ( - <span - className={styles.buttonWrapper} - onClick={onClick} - > - {label} - </span> - ); -}; diff --git a/control-station/src/components/ButtonTag/ButtonTag.module.scss b/control-station/src/components/ButtonTag/ButtonTag.module.scss deleted file mode 100644 index 1b27d73bc..000000000 --- a/control-station/src/components/ButtonTag/ButtonTag.module.scss +++ /dev/null @@ -1,89 +0,0 @@ -@use "src/styles/colors"; - -$button-height: 2rem; -$icon-size: 0.8rem; -$label-size: 0.5rem; - -$inner-shadow: inset 0 2px colors.getColor("tertiary", 60); - -.buttonTagWrapper { - display: flex; - flex-flow: column; - align-items: center; - height: fit-content; - width: 100%; - - p { - transition: all 90ms ease-out; - margin-top: 5px; - margin-bottom: 0px; - font-size: $label-size; - font-weight: 600; - text-align: center; - user-select: none; - } - - button { - appearance: none; - margin-top: 0.5rem; - - width: 100%; - height: $button-height; - transform: translateY(-8px); - - border: none; - border-radius: 6px; - box-shadow: 0 5px; - - transition: all 30ms ease-out; - - font-size: $icon-size; - font-weight: bold; - - display: flex; - align-items: center; - justify-content: center; - } -} - -.disabled { - p { - color: colors.getColor("neutral", 70); - } - - button { - color: colors.getColor("neutral", 70); - background-color: colors.getColor("neutral", 90); - } -} - -.enabled { - p { - color: colors.getColor("primary", 60); - } - - &:hover p { - color: colors.getColor("primary", 50); - } - - &:active p { - color: colors.getColor("tertiary", 50); - } - - button { - color: colors.getColor("primary", 60); - background-color: colors.getColor("primary", 90); - - &:hover { - color: colors.getColor("primary", 50); - background-color: colors.getColor("primary", 80); - } - - &:active { - color: colors.getColor("tertiary", 50); - background-color: colors.getColor("tertiary", 80); - box-shadow: none; - transform: translateY(0px); - } - } -} diff --git a/control-station/src/components/ButtonTag/ButtonTag.tsx b/control-station/src/components/ButtonTag/ButtonTag.tsx deleted file mode 100644 index 67613f227..000000000 --- a/control-station/src/components/ButtonTag/ButtonTag.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { ButtonHTMLAttributes, DetailedHTMLProps, ReactNode } from "react"; -import style from "./ButtonTag.module.scss"; - -type Props = { - label?: string; - icon: ReactNode; - onClick: () => void; -} & Omit< - DetailedHTMLProps< - ButtonHTMLAttributes<HTMLButtonElement>, - HTMLButtonElement - >, - "onClick" ->; - -export function ButtonTag({ - icon, - label, - onClick, - disabled, - ...buttonProps -}: Props) { - return ( - <label - className={`${style.buttonTagWrapper} ${ - disabled ? style.disabled : style.enabled - }`} - > - <button - {...buttonProps} - onClick={() => { - if (!disabled) { - onClick(); - } - }} - > - {icon} - </button> - {label && <p>{label}</p>} - </label> - ); -} diff --git a/control-station/src/components/EmergencyOrders/EmergencyButton/EmergencyButton.module.scss b/control-station/src/components/EmergencyOrders/EmergencyButton/EmergencyButton.module.scss deleted file mode 100644 index 9f1eaa0a0..000000000 --- a/control-station/src/components/EmergencyOrders/EmergencyButton/EmergencyButton.module.scss +++ /dev/null @@ -1,21 +0,0 @@ -@use "src/styles/fonts"; - -.emergencyButtonWrapper { - padding: 1rem; - display: flex; - flex-direction: column; - gap: 1rem; - height: fit-content; - align-items: center; - border-radius: 1rem; - cursor: pointer; - filter: var(--shadow); -} - -.label { - color: white; - text-align: center; - font-size: fonts.getFontSize("large"); - font-weight: fonts.getFontWeight("bold"); - line-height: 90%; -} diff --git a/control-station/src/components/EmergencyOrders/EmergencyButton/EmergencyButton.tsx b/control-station/src/components/EmergencyOrders/EmergencyButton/EmergencyButton.tsx deleted file mode 100644 index a70f0fcb4..000000000 --- a/control-station/src/components/EmergencyOrders/EmergencyButton/EmergencyButton.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import styles from "./EmergencyButton.module.scss"; -import { useListenKey } from "common"; - -type Props = { - label: string; - icon: React.ReactNode; - className: string; - targetKey: string; - onTrigger: () => void; -}; - -export const EmergencyButton = ({ - label, - icon, - className, - targetKey, - onTrigger, -}: Props) => { - useListenKey(targetKey, onTrigger, true); - - return ( - <div - className={`${styles.emergencyButtonWrapper} ${className}`} - onClick={onTrigger} - > - {icon} - <span className={styles.label}>{label}</span> - </div> - ); -}; diff --git a/control-station/src/components/EmergencyOrders/EmergencyOrders.module.scss b/control-station/src/components/EmergencyOrders/EmergencyOrders.module.scss deleted file mode 100644 index cffb01119..000000000 --- a/control-station/src/components/EmergencyOrders/EmergencyOrders.module.scss +++ /dev/null @@ -1,37 +0,0 @@ -.emergencyOrdersWrapper { - height: fit-content; - - display: flex; - flex-direction: column; - align-items: stretch; - gap: 1rem; - font-family: var(--font-mono); -} - -$stop-color: #ff2d2d; -$restart-color: #2d74ff; -$brake-color: #ff962d; -$contactor-color: #ffdc2d; - -.icon { - font-size: 1.5rem; -} - -.stopBtn { - background-color: $stop-color; -} - -.restartBtn { - background-color: $restart-color; -} - -.brakeBtn { - background-color: $brake-color; -} - -.contactorBtn { - background-color: $contactor-color; - > * { - color: black !important; - } -} diff --git a/control-station/src/components/EmergencyOrders/EmergencyOrders.tsx b/control-station/src/components/EmergencyOrders/EmergencyOrders.tsx deleted file mode 100644 index b16a82d7a..000000000 --- a/control-station/src/components/EmergencyOrders/EmergencyOrders.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import styles from "./EmergencyOrders.module.scss"; -import { EmergencyButton } from "./EmergencyButton/EmergencyButton"; -import { ReactComponent as StopIcon } from "assets/svg/stop_icon.svg"; -import { ReactComponent as RestartIcon } from "assets/svg/restart_icon.svg"; -import { ReactComponent as BrakeIcon } from "assets/svg/brake.svg"; -import { ReactComponent as ContactorIcon } from "assets/svg/open_switch.svg"; - -type Props = { - stop: () => void; - reset: () => void; - brake: () => void; - openContactors: () => void; -}; - -export const EmergencyOrders = ({ - brake, - openContactors, - reset, - stop, -}: Props) => { - const StyledStopIcon = <StopIcon className={styles.icon} />; - const StyledRestartIcon = ( - <RestartIcon - className={`${styles.icon}`} - color="#ebf6ff" - /> - ); - - const StyledBrakeIcon = <BrakeIcon className={styles.icon} />; - const StyledOpenContactorIcon = ( - <ContactorIcon - className={styles.icon} - color="black" - /> - ); - return ( - <div className={styles.emergencyOrdersWrapper}> - <EmergencyButton - label={"STOP"} - icon={StyledStopIcon} - className={styles.stopBtn} - targetKey="s" - onTrigger={stop} - /> - <EmergencyButton - label={"RESET"} - icon={StyledRestartIcon} - className={styles.restartBtn} - targetKey="r" - onTrigger={reset} - /> - <EmergencyButton - label={"BRAKE"} - icon={StyledBrakeIcon} - className={styles.brakeBtn} - targetKey="b" - onTrigger={brake} - /> - <EmergencyButton - label={"OPEN CONTACTORS"} - icon={StyledOpenContactorIcon} - className={styles.contactorBtn} - targetKey="o" - onTrigger={openContactors} - /> - </div> - ); -}; diff --git a/control-station/src/components/GaugeTag/Gauge/BackgroundArc/BackgroundArc.tsx b/control-station/src/components/GaugeTag/Gauge/BackgroundArc/BackgroundArc.tsx index 7d72a31af..875ed75a2 100644 --- a/control-station/src/components/GaugeTag/Gauge/BackgroundArc/BackgroundArc.tsx +++ b/control-station/src/components/GaugeTag/Gauge/BackgroundArc/BackgroundArc.tsx @@ -1,9 +1,10 @@ -import styles from "components/GaugeTag/Gauge/BackgroundArc/BackgroundArc.module.scss"; -import { Arc } from "components/GaugeTag/Gauge/Arc/Arc"; -type Props = React.ComponentProps<typeof Arc>; +import styles from 'components/GaugeTag/Gauge/BackgroundArc/BackgroundArc.module.scss'; +import { Arc } from 'components/GaugeTag/Gauge/Arc/Arc'; +type Props = React.ComponentProps<typeof Arc> & { id: string }; export const BackgroundArc = ({ percentage, + id, radius, strokeWidth, sweep, @@ -12,7 +13,7 @@ export const BackgroundArc = ({ return ( <> <defs> - <mask id="myMask"> + <mask id={id}> <Arc sweep={sweep} radius={radius} @@ -28,13 +29,13 @@ export const BackgroundArc = ({ y="0" width="100%" height="100%" - mask="url(#myMask)" + mask={`url(#${id})`} > <div className={className} style={{ - width: "100%", - height: "100%", + width: '100%', + height: '100%', }} /> </foreignObject> diff --git a/control-station/src/components/GaugeTag/Gauge/Gauge.tsx b/control-station/src/components/GaugeTag/Gauge/Gauge.tsx index d1c8bd5f2..a7128cea8 100644 --- a/control-station/src/components/GaugeTag/Gauge/Gauge.tsx +++ b/control-station/src/components/GaugeTag/Gauge/Gauge.tsx @@ -1,34 +1,30 @@ -import { clampAndNormalize } from "math"; -import { Arc } from "./Arc/Arc"; -import { BackgroundArc } from "./BackgroundArc/BackgroundArc"; -import styles from "components/GaugeTag/Gauge/Gauge.module.scss"; +import { clampAndNormalize } from 'math'; +import { Arc } from './Arc/Arc'; +import { BackgroundArc } from './BackgroundArc/BackgroundArc'; +import styles from 'components/GaugeTag/Gauge/Gauge.module.scss'; type Props = { className: string; + id: string; sweep: number; strokeWidth: number; - value: number; - min: number; - max: number; + percentage: number; }; export const Gauge = ({ className, + id, sweep, strokeWidth, - value, - min, - max, + percentage, }: Props) => { const radius = 500; - const percentage = clampAndNormalize(value, min, max) * 100; return ( <svg className={className} width="1em" height="1em" viewBox={`0 0 ${radius * 2} ${radius * 2}`} - xmlns="http://www.w3.org/2000/svg" > <Arc className={styles.backgroundArc} @@ -41,6 +37,7 @@ export const Gauge = ({ <BackgroundArc sweep={sweep} className={styles.rainbowArc} + id={id} percentage={percentage} radius={radius} strokeWidth={strokeWidth} diff --git a/control-station/src/components/GaugeTag/GaugeTag.tsx b/control-station/src/components/GaugeTag/GaugeTag.tsx index 436da642d..0022d0a45 100644 --- a/control-station/src/components/GaugeTag/GaugeTag.tsx +++ b/control-station/src/components/GaugeTag/GaugeTag.tsx @@ -1,47 +1,43 @@ -import styles from "components/GaugeTag/GaugeTag.module.scss"; -import { Gauge } from "components/GaugeTag/Gauge/Gauge"; -import { TextData } from "./TextData/TextData"; -import { memo, useState } from "react"; -import { useGlobalTicker } from "common"; +import styles from 'components/GaugeTag/GaugeTag.module.scss'; +import { Gauge } from 'components/GaugeTag/Gauge/Gauge'; +import { TextData } from './TextData/TextData'; +import { memo, useEffect, useRef, useState } from 'react'; +import { useGlobalTicker } from 'common'; +import { getPercentageFromRange } from 'state'; type Props = { name: string; + id: string; units: string; getUpdate: () => number; strokeWidth: number; min: number; max: number; }; -export const GaugeTag = memo(({ - name, - units, - getUpdate, - strokeWidth, - min, - max, -}: Props) => { +export const GaugeTag = memo( + ({ name, units, id, getUpdate, strokeWidth, min, max }: Props) => { + const [value, setValue] = useState(getUpdate()); + const percentage = useRef(0); - const [value, setValue] = useState(getUpdate()); + useGlobalTicker(() => { + setValue(getUpdate()); + }); - useGlobalTicker(() => { - setValue(getUpdate()); - }) + useEffect(() => { + percentage.current = getPercentageFromRange(value, min, max); + }); - return ( - <article className={styles.gaugeTagWrapper}> - <Gauge - className={styles.gauge} - sweep={250} - strokeWidth={strokeWidth} - value={value} - min={min} - max={max} - /> - <TextData - name={name} - units={units} - value={value} - ></TextData> - </article> - ); -}); + return ( + <article className={styles.gaugeTagWrapper}> + <Gauge + className={styles.gauge} + id={id} + sweep={250} + strokeWidth={strokeWidth} + percentage={percentage.current} + /> + <TextData name={name} units={units} value={value}></TextData> + </article> + ); + } +); diff --git a/control-station/src/components/IndicatorStack/IndicatorStack.module.scss b/control-station/src/components/IndicatorStack/IndicatorStack.module.scss index 54e55816f..5b74fafc9 100644 --- a/control-station/src/components/IndicatorStack/IndicatorStack.module.scss +++ b/control-station/src/components/IndicatorStack/IndicatorStack.module.scss @@ -6,8 +6,9 @@ align-items: center; justify-content: center; overflow: hidden; - margin: .1rem 0; - + margin: 0.1rem 0; + border-radius: 1rem; + > div { border-bottom: 1.5px solid hsl(0, 0%, 35%); } @@ -25,4 +26,4 @@ border-bottom-left-radius: 1rem; border-bottom-right-radius: 1rem; } -} \ No newline at end of file +} diff --git a/control-station/src/components/IndicatorStack/IndicatorStack.tsx b/control-station/src/components/IndicatorStack/IndicatorStack.tsx index 6cd5eb880..6048e2c89 100644 --- a/control-station/src/components/IndicatorStack/IndicatorStack.tsx +++ b/control-station/src/components/IndicatorStack/IndicatorStack.tsx @@ -1,13 +1,10 @@ -import styles from "./IndicatorStack.module.scss" +import styles from './IndicatorStack.module.scss'; interface Props { children: React.ReactNode; + className?: string; } -export const IndicatorStack = ({children}: Props) => { - return ( - <div className={styles.container}> - {children} - </div> - ) -} +export const IndicatorStack = ({ children, className }: Props) => { + return <div className={`${styles.container} ${className}`}>{children}</div>; +}; diff --git a/control-station/src/components/InputTag/BackendTypes.ts b/control-station/src/components/InputTag/BackendTypes.ts deleted file mode 100644 index 411456188..000000000 --- a/control-station/src/components/InputTag/BackendTypes.ts +++ /dev/null @@ -1,46 +0,0 @@ -//FIXME: Add in "common" - -export type BackendType = NumericType | Bool | Enum; - -export type NumericType = - | SignedIntegerType - | UnsignedIntegerType - | FloatingType; - -export type SignedIntegerType = "int8" | "int16" | "int32" | "int64"; - -export type UnsignedIntegerType = "uint8" | "uint16" | "uint32" | "uint64"; - -export type FloatingType = "float32" | "float64"; - -export function isNumericType(type: string): type is NumericType { - return ( - isUnsignedIntegerType(type) || - isSignedIntegerType(type) || - isFloatingType(type) - ); -} - -export function isUnsignedIntegerType( - type: string -): type is UnsignedIntegerType { - return ( - type == "uint8" || - type == "uint16" || - type == "uint32" || - type == "uint64" - ); -} - -export function isSignedIntegerType(type: string): type is SignedIntegerType { - return ( - type == "int8" || type == "int16" || type == "int32" || type == "int64" - ); -} - -export function isFloatingType(type: string): type is FloatingType { - return type == "float32" || type == "float64"; -} - -type Enum = "Enum"; -type Bool = "bool"; diff --git a/control-station/src/components/InputTag/InputTag.module.scss b/control-station/src/components/InputTag/InputTag.module.scss deleted file mode 100644 index 8d147f77a..000000000 --- a/control-station/src/components/InputTag/InputTag.module.scss +++ /dev/null @@ -1,63 +0,0 @@ -@use "src/styles/colors"; - -.inputTagWrapper { - height: fit-content; - transition: border-color 100ms ease-in; - - border: 2px solid; - border-radius: 6px; - - input { - appearance: none; - border: none; - - background-color: transparent; - - width: 100%; - - font-size: 0.75rem; - - color: colors.getColor("neutral", 30); - - &:focus { - outline: none; - } - - &::-webkit-outer-spin-button, - &::-webkit-inner-spin-button { - -webkit-appearance: none; - } - - -moz-appearance: textfield; - } - - legend { - transition: color 100ms ease-in; - - font-weight: 500; - - padding: 0 0.5rem; - - background-color: transparent; - } -} - -.on { - border-color: colors.getColor("tertiary", 70); - flex-grow: 1; - height: 2rem; - - legend { - color: colors.getColor("tertiary", 70); - } -} - -.off { - border-color: colors.getColor("neutral", 70); - flex-grow: 1; - height: 2rem; - - legend { - color: colors.getColor("neutral", 70); - } -} diff --git a/control-station/src/components/InputTag/InputTag.tsx b/control-station/src/components/InputTag/InputTag.tsx deleted file mode 100644 index d5d7c8b28..000000000 --- a/control-station/src/components/InputTag/InputTag.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { DetailedHTMLProps, InputHTMLAttributes } from "react"; -import style from "./InputTag.module.scss"; -import { isNumberValid } from "./validation"; - -type Props = { - id: string; - disabled: boolean; - onChange: (state: number) => void; -} & Omit< - DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, - "onChange" | "disabled" ->; - -const onChangeInput = ( - e: React.FormEvent<HTMLInputElement>, - onChange: (state: number) => void -) => { - const currentNumber = Number.parseFloat(e.currentTarget.value); - if (isNumberValid(e.currentTarget.value, "float64")) { - onChange(currentNumber); - } else if (e.currentTarget.value == "") { - onChange(currentNumber); - } else { - //TODO: don't print the key in the input - } -}; - -export function InputTag({ id, disabled, onChange, ...inputProps }: Props) { - return ( - <fieldset - className={`${style.inputTagWrapper} ${ - disabled ? style.off : style.on - }`} - > - <legend className={style.testInputLabel}>{id}</legend> - <input - onChange={(e: React.FormEvent<HTMLInputElement>) => { - onChangeInput(e, onChange); - }} - {...inputProps} - ></input> - </fieldset> - ); -} diff --git a/control-station/src/components/InputTag/validation.ts b/control-station/src/components/InputTag/validation.ts deleted file mode 100644 index 5c2e8a803..000000000 --- a/control-station/src/components/InputTag/validation.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { - NumericType, - isSignedIntegerType, - isUnsignedIntegerType, -} from "./BackendTypes"; - -//FIXME: Add in "common" -export function isNumberValid( - valueStr: string, - numberType: NumericType -): boolean { - return ( - checkNumberString(valueStr, numberType) && - ((isUnsignedIntegerType(numberType) && - checkUnsignedIntegerOverflow( - Number.parseInt(valueStr), - getBits(numberType) - )) || - (isSignedIntegerType(numberType) && - checkSignedIntegerOverflow( - Number.parseInt(valueStr), - getBits(numberType) - )) || - checkFloatOverflow(Number.parseFloat(valueStr))) - ); -} - -function checkNumberString(valueStr: string, numberType: NumericType): boolean { - if (isUnsignedIntegerType(numberType)) { - return /^\d+$/.test(valueStr); - } else if (isSignedIntegerType(numberType)) { - return /^-?\d+$/.test(valueStr); - } else { - return /^-?\d+(?:\.\d+)?$/.test(valueStr); - } -} - -function checkUnsignedIntegerOverflow(value: number, bits: number): boolean { - return value >= 0 && value < 1 << bits; //FIXME: añadir unos -} - -function checkSignedIntegerOverflow(value: number, bits: number): boolean { - return value >= -1 << (bits - 1) && value < 1 << (bits - 1); -} - -function checkFloatOverflow(value: number): boolean { - return !Number.isNaN(value); -} - -function getBits(type: NumericType): number { - switch (type) { - case "uint8": - return 8; - case "uint16": - return 16; - case "uint32": - return 32; - case "uint64": - return 64; - case "int8": - return 8; - case "int16": - return 16; - case "int32": - return 32; - case "int64": - return 64; - case "float32": - return 32; - case "float64": - return 64; - } -} diff --git a/control-station/src/components/InstructionButton/InstructionButton.module.scss b/control-station/src/components/InstructionButton/InstructionButton.module.scss deleted file mode 100644 index 5d93bbdb0..000000000 --- a/control-station/src/components/InstructionButton/InstructionButton.module.scss +++ /dev/null @@ -1,79 +0,0 @@ -@use "src/styles/colors"; - -$button-height: 2rem; -$button-width: 2.25rem; -$icon-size: 1rem; -$label-size: 0.5rem; - -.toggleButtonWrapper { - display: flex; - flex-flow: column; - - button { - flex-grow: 1; - flex-flow: column; - - font-size: $icon-size; - font-weight: bold; - - appearance: none; - border: none; - border-radius: 6px; - - background-color: transparent; - - transition: box-shadow 30ms ease-in, transform 30ms ease-in; - - display: flex; - align-items: center; - justify-content: center; - - &:active { - box-shadow: none; - transform: translateY(0px); - } - } - - p { - margin-top: 5px; - margin-bottom: 0px; - font-size: $label-size; - font-weight: 600; - text-align: center; - user-select: none; - } -} - -.on { - * { - color: colors.getColor("tertiary", 60); - } - - button { - background-color: colors.getColor("tertiary", 90); - box-shadow: 0 4px colors.getColor("tertiary", 60); - transform: translateY(-4px); - - &:hover { - background-color: colors.getColor("tertiary", 80); - color: colors.getColor("tertiary", 50); - } - } -} - -.off { - * { - color: colors.getColor("primary", 60); - } - - button { - background-color: colors.getColor("primary", 90); - box-shadow: 0 5px colors.getColor("primary", 60); - transform: translateY(-8px); - - &:hover { - background-color: colors.getColor("primary", 80); - color: colors.getColor("primary", 50); - } - } -} diff --git a/control-station/src/components/InstructionButton/InstructionButton.tsx b/control-station/src/components/InstructionButton/InstructionButton.tsx deleted file mode 100644 index f01b8243c..000000000 --- a/control-station/src/components/InstructionButton/InstructionButton.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { useToggle } from "hooks/useToggle"; -import { - ButtonHTMLAttributes, - DetailedHTMLProps, - ReactNode, - useEffect, -} from "react"; -import { SendJsonMessage } from "react-use-websocket/dist/lib/types"; -import style from "./InstructionButton.module.scss"; - -type ControlOrder = { - id: number; - state: boolean; -}; - -type Props = { - id: number; - icon: ReactNode; - sendJsonMessage: SendJsonMessage; - onToggle?: (state: boolean) => void; -} & Omit< - DetailedHTMLProps< - ButtonHTMLAttributes<HTMLButtonElement>, - HTMLButtonElement - >, - "onClick" | "id" ->; - -export function InstructionButton({ - id, - icon, - sendJsonMessage, - onToggle, - ...buttonProps -}: Props) { - const [isOn, flip] = useToggle(false); - - useEffect(() => { - onToggle?.(isOn); - }, [isOn]); - - const labelClass = `${style.toggleButtonWrapper} ${ - isOn ? style.on : style.off - }`; - return ( - <label className={labelClass}> - <button - onClick={() => { - flip(); - sendOrder(!isOn, id, sendJsonMessage); - }} - {...buttonProps} - > - {icon} - <p>Custom {id}</p> - </button> - </label> - ); -} - -function sendOrder( - isOn: boolean, - id: number, - sendJsonMessage: SendJsonMessage -) { - const controlOrder: ControlOrder = { id: id, state: isOn }; - sendJsonMessage(controlOrder); -} diff --git a/control-station/src/components/LevitationUnit/EMSRepresentation/EMSRepresentation.module.scss b/control-station/src/components/LevitationUnit/EMSRepresentation/EMSRepresentation.module.scss new file mode 100644 index 000000000..51a12e0e8 --- /dev/null +++ b/control-station/src/components/LevitationUnit/EMSRepresentation/EMSRepresentation.module.scss @@ -0,0 +1,28 @@ +.container { + display: flex; + flex-flow: row; + max-height: 103px; + max-width: 120px; + overflow: hidden; +} + +.rotated { + transform: rotate(180deg); +} + +.wall { + > img { + height: 100%; + } +} + +.unit { + position: relative; + width: 47px; + + > img { + position: absolute; + + height: 100%; + } +} diff --git a/control-station/src/components/LevitationUnit/EMSRepresentation/EMSRepresentation.tsx b/control-station/src/components/LevitationUnit/EMSRepresentation/EMSRepresentation.tsx new file mode 100644 index 000000000..0dc082108 --- /dev/null +++ b/control-station/src/components/LevitationUnit/EMSRepresentation/EMSRepresentation.tsx @@ -0,0 +1,52 @@ +import { NumericMeasurementInfo, useGlobalTicker } from 'common'; +import styles from './EMSRepresentation.module.scss'; +import { useEffect, useRef, useState } from 'react'; +import { getPercentageFromRange } from 'state'; +import EMSWall from '../../../assets/svg/EMS-wall.svg'; +import EMS from '../../../assets/svg/EMS.svg'; + +type Props = { + getUpdate: () => number; + rangeMin: number; + rangeMax: number; + side: 'left' | 'right'; +}; + +export default function EMSRepresentation(props: Props) { + const [valueState, setValueState] = useState<number>(0); + const percentage = useRef<number>(100); + + useGlobalTicker(() => { + setValueState(props.getUpdate()); + }); + + useEffect(() => { + percentage.current = getPercentageFromRange( + valueState, + props.rangeMin, + props.rangeMax + ); + }); + + return ( + <div + className={`${styles.container} ${ + props.side == 'right' ? styles.rotated : '' + }`} + > + <div className={styles.wall}> + <img src={EMSWall} alt="wall" /> + </div> + + <div className={styles.unit}> + <img + src={EMS} + alt="ems" + style={{ + left: percentage.current + '%', + }} + /> + </div> + </div> + ); +} diff --git a/control-station/src/components/LevitationUnit/HEMSRepresentation/HEMSRepresentation.module.scss b/control-station/src/components/LevitationUnit/HEMSRepresentation/HEMSRepresentation.module.scss new file mode 100644 index 000000000..c37e66513 --- /dev/null +++ b/control-station/src/components/LevitationUnit/HEMSRepresentation/HEMSRepresentation.module.scss @@ -0,0 +1,24 @@ +.container { + display: flex; + flex-flow: column; + max-height: 103px; + max-width: 120px; + overflow: hidden; +} + +.wall { + > img { + width: 100%; + } +} + +.unit { + position: relative; + height: 57px; + + > img { + position: absolute; + + width: 100%; + } +} diff --git a/control-station/src/components/LevitationUnit/HEMSRepresentation/HEMSRepresentation.tsx b/control-station/src/components/LevitationUnit/HEMSRepresentation/HEMSRepresentation.tsx new file mode 100644 index 000000000..265de02c4 --- /dev/null +++ b/control-station/src/components/LevitationUnit/HEMSRepresentation/HEMSRepresentation.tsx @@ -0,0 +1,47 @@ +import { NumericMeasurementInfo, useGlobalTicker } from 'common'; +import styles from './HEMSRepresentation.module.scss'; +import HEMSWall from '../../../assets/svg/HEMS-wall.svg'; +import HEMS from '../../../assets/svg/HEMS.svg'; +import { useEffect, useRef, useState } from 'react'; +import { getPercentageFromRange } from 'state'; + +type Props = { + getUpdate: () => number; + rangeMin: number; + rangeMax: number; +}; + +export default function HEMSRepresentation(props: Props) { + const [valueState, setValueState] = useState<number>(0); + const percentage = useRef<number>(100); + + useGlobalTicker(() => { + setValueState(props.getUpdate()); + }); + + useEffect(() => { + percentage.current = getPercentageFromRange( + valueState, + props.rangeMin, + props.rangeMax + ); + }); + + return ( + <div className={styles.container}> + <div className={styles.wall}> + <img src={HEMSWall} alt="wall" /> + </div> + + <div className={styles.unit}> + <img + src={HEMS} + alt="hems" + style={{ + top: percentage.current + '%', + }} + /> + </div> + </div> + ); +} diff --git a/control-station/src/components/LevitationUnit/LevitationUnit.tsx b/control-station/src/components/LevitationUnit/LevitationUnit.tsx index 10669df14..0ce58cdc8 100644 --- a/control-station/src/components/LevitationUnit/LevitationUnit.tsx +++ b/control-station/src/components/LevitationUnit/LevitationUnit.tsx @@ -1,68 +1,181 @@ -import styles from "./LevitationUnit.module.scss" -import { IndicatorStack } from "components/IndicatorStack/IndicatorStack" -import { BarIndicator } from "components/BarIndicator/BarIndicator" -import batteryFilled from "assets/svg/battery-filled.svg" -import thermometerFilled from "assets/svg/thermometer-filled.svg" - -interface Props { - measurementId?: string, - imageSide: "left" | "right", - imgSrc: string, - rotate?: boolean +import { LcuMeasurements } from 'common'; +import styles from './LevitationUnit.module.scss'; +import { IndicatorStack } from 'components/IndicatorStack/IndicatorStack'; +import { BarIndicator } from 'components/BarIndicator/BarIndicator'; +import batteryFilled from 'assets/svg/battery-filled.svg'; +import thermometerFilled from 'assets/svg/thermometer-filled.svg'; +import airgapIcon from 'assets/svg/z-index.svg'; +import { useMeasurementsStore } from 'common'; +import EMSRepresentation from './EMSRepresentation/EMSRepresentation'; +import HEMSRepresentation from './HEMSRepresentation/HEMSRepresentation'; +export interface Props { + unitIndex: number; + imageSide: 'left' | 'right'; + kind: 'ems' | 'hems'; } -export const LevitationUnit = ({ - measurementId, - imgSrc, - imageSide, - rotate -}: Props) => { +export const currentMeasurements = [ + LcuMeasurements.coilCurrentHEMS1, + LcuMeasurements.coilCurrentHEMS2, + LcuMeasurements.coilCurrentHEMS3, + LcuMeasurements.coilCurrentHEMS4, + LcuMeasurements.coilCurrentEMS1, + LcuMeasurements.coilCurrentEMS2, + LcuMeasurements.coilCurrentEMS3, + LcuMeasurements.coilCurrentEMS4, + LcuMeasurements.coilCurrentEMS5, + LcuMeasurements.coilCurrentEMS6, +]; + +export const temperatureMeasurements = [ + LcuMeasurements.coilTemperatureHEMS1, + LcuMeasurements.coilTemperatureHEMS2, + LcuMeasurements.coilTemperatureHEMS3, + LcuMeasurements.coilTemperatureHEMS4, + LcuMeasurements.coilTemperatureEMS1, + LcuMeasurements.coilTemperatureEMS2, + LcuMeasurements.coilTemperatureEMS3, + LcuMeasurements.coilTemperatureEMS4, + LcuMeasurements.coilTemperatureEMS5, + LcuMeasurements.coilTemperatureEMS6, +]; + +export const airgapMeasurements = [ + LcuMeasurements.verticalAirgap1, + LcuMeasurements.verticalAirgap2, + LcuMeasurements.verticalAirgap3, + LcuMeasurements.verticalAirgap4, + LcuMeasurements.horizontalAirgap1, + LcuMeasurements.horizontalAirgap2, + LcuMeasurements.horizontalAirgap1, + LcuMeasurements.horizontalAirgap2, + LcuMeasurements.horizontalAirgap3, + LcuMeasurements.horizontalAirgap4, +]; + +export const LevitationUnit = ({ unitIndex, kind, imageSide }: Props) => { + const getNumericMeasurementInfo = useMeasurementsStore( + (state) => state.getNumericMeasurementInfo + ); + + const current = getNumericMeasurementInfo(currentMeasurements[unitIndex]); + const temperature = getNumericMeasurementInfo( + temperatureMeasurements[unitIndex] + ); + const airgap = getNumericMeasurementInfo(airgapMeasurements[unitIndex]); + const airgap2 = + unitIndex == 6 || unitIndex == 7 + ? getNumericMeasurementInfo(airgapMeasurements[unitIndex + 2]) + : undefined; + + console.log( + unitIndex, + currentMeasurements[unitIndex], + temperatureMeasurements[unitIndex], + airgapMeasurements[unitIndex], + current, + temperature, + airgap + ); + return ( <div className={styles.levitationUnitWrapper}> - {imageSide === "left" && ( - <div className={styles.levitationUnitImage}> - <img - style={{ transform: rotate ? "rotate(180deg)" : "" }} - src={imgSrc} - alt="Levitation Unit" + {imageSide === 'left' && + (kind == 'ems' ? ( + <EMSRepresentation + getUpdate={ + unitIndex == 6 || unitIndex == 7 + ? () => + (airgap.getUpdate() + + airgap2!.getUpdate()) / + 2 + : airgap.getUpdate + } + rangeMin={airgap.warningRange[0] ?? 0} + rangeMax={airgap.warningRange[1] ?? 0} + side={imageSide} /> - </div> - )} - <IndicatorStack> - <BarIndicator - icon={batteryFilled} - title="Current" - getValue={() => 0} - units="A" - safeRangeMin={0} - safeRangeMax={10} + ) : ( + <HEMSRepresentation + getUpdate={ + unitIndex == 6 || unitIndex == 7 + ? () => + (airgap.getUpdate() + + airgap2!.getUpdate()) / + 2 + : airgap.getUpdate + } + rangeMin={airgap.warningRange[0] ?? 0} + rangeMax={airgap.warningRange[1] ?? 0} /> - <BarIndicator - icon={thermometerFilled} - title="Temperature" - getValue={() => 0} - units="ºC" - safeRangeMin={0} - safeRangeMax={10} + ))} + <IndicatorStack> + <BarIndicator + icon={batteryFilled} + title="Current" + getValue={current.getUpdate} + units={current.units} + safeRangeMin={current.range[0] ?? -25} + safeRangeMax={current.range[1] ?? 25} + warningRangeMin={current.warningRange[0] ?? -50} + warningRangeMax={current.warningRange[1] ?? 50} + /> + <BarIndicator + icon={thermometerFilled} + title="Temperature" + getValue={temperature.getUpdate} + units={temperature.units} + safeRangeMin={temperature.range[0] ?? 0} + safeRangeMax={temperature.range[1] ?? 40} + warningRangeMin={temperature.warningRange[0] ?? -10} + warningRangeMax={temperature.warningRange[1] ?? 80} + /> + <BarIndicator + icon={airgapIcon} + title="Airgap" + getValue={ + unitIndex == 6 || unitIndex == 7 + ? () => + (airgap.getUpdate() + airgap2!.getUpdate()) / + 2 + : airgap.getUpdate + } + units={airgap.units} + safeRangeMin={airgap.range[0] ?? 0} + safeRangeMax={airgap.range[1] ?? 0} + warningRangeMin={airgap.warningRange[0] ?? 0} + warningRangeMax={airgap.warningRange[1] ?? 0} + /> + </IndicatorStack> + {imageSide === 'right' && + (kind == 'ems' ? ( + <EMSRepresentation + getUpdate={ + unitIndex == 6 || unitIndex == 7 + ? () => + (airgap.getUpdate() + + airgap2!.getUpdate()) / + 2 + : airgap.getUpdate + } + rangeMin={airgap.warningRange[0] ?? 0} + rangeMax={airgap.warningRange[1] ?? 0} + side={imageSide} /> - <BarIndicator - icon={batteryFilled} - title="Airgap" - getValue={() => 0} - units="mm" - safeRangeMin={0} - safeRangeMax={10} + ) : ( + <HEMSRepresentation + getUpdate={ + unitIndex == 6 || unitIndex == 7 + ? () => + (airgap.getUpdate() + + airgap2!.getUpdate()) / + 2 + : airgap.getUpdate + } + rangeMin={airgap.warningRange[0] ?? 0} + rangeMax={airgap.warningRange[1] ?? 0} /> - </IndicatorStack> - {imageSide === "right" && ( - <div className={styles.levitationUnitImage}> - <img - style={{ transform: rotate ? "rotate(180deg)" : "" }} - src={imgSrc} - alt="Levitation Unit" - /> - </div> - )} + ))} </div> - ) -} + ); +}; diff --git a/control-station/src/components/MultipleTags/MultipleTags.module.scss b/control-station/src/components/MultipleTags/MultipleTags.module.scss deleted file mode 100644 index 599f489e1..000000000 --- a/control-station/src/components/MultipleTags/MultipleTags.module.scss +++ /dev/null @@ -1,6 +0,0 @@ -.multipleTagsWrapper { - display: flex; - flex-direction: row; - justify-content: center; - height: fit-content; -} diff --git a/control-station/src/components/MultipleTags/MultipleTags.tsx b/control-station/src/components/MultipleTags/MultipleTags.tsx deleted file mode 100644 index e96c769ac..000000000 --- a/control-station/src/components/MultipleTags/MultipleTags.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import styles from "./MultipleTags.module.scss"; -import { Separator } from "./Separator/Separator"; -import React from "react"; -type Props = { - tags: React.ReactNode[]; - className?: string; -}; - -export const MultipleTags = ({ tags, className = "" }: Props) => { - return ( - <article className={`${styles.multipleTagsWrapper} ${className}`}> - {tags.map((tag, index, arr) => { - return ( - <React.Fragment key={index}> - {tag} - {/* FIXME: el separator no es 100% height a menos que el height del padre sea definite */} - {index < arr.length - 1 && <Separator />} - </React.Fragment> - ); - })} - </article> - ); -}; diff --git a/control-station/src/components/MultipleTags/Separator/Separator.module.scss b/control-station/src/components/MultipleTags/Separator/Separator.module.scss deleted file mode 100644 index 2eb3303e4..000000000 --- a/control-station/src/components/MultipleTags/Separator/Separator.module.scss +++ /dev/null @@ -1,15 +0,0 @@ -.separatorWrapper { - display: flex; - justify-content: center; - align-items: center; - width: 3px; - height: 100%; - - .line { - width: 50%; - height: 100%; - padding: 1px; - background-color: rgb(215, 215, 215); - border-radius: 1rem; - } -} diff --git a/control-station/src/components/MultipleTags/Separator/Separator.tsx b/control-station/src/components/MultipleTags/Separator/Separator.tsx deleted file mode 100644 index 721667edf..000000000 --- a/control-station/src/components/MultipleTags/Separator/Separator.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import styles from "./Separator.module.scss"; - -export const Separator = () => { - return ( - <div className={styles.separatorWrapper}> - <div className={styles.line}></div> - </div> - ); -}; diff --git a/control-station/src/components/Sidebar/Sidebar.module.scss b/control-station/src/components/Sidebar/Sidebar.module.scss index 0e36f6414..128bc8b03 100644 --- a/control-station/src/components/Sidebar/Sidebar.module.scss +++ b/control-station/src/components/Sidebar/Sidebar.module.scss @@ -1,33 +1,47 @@ -@use "src/styles/colors"; +@use 'src/styles/colors'; -.sidebarWrapper { - width: fit-content; +.sidebar { display: flex; - flex-direction: column; + flex-flow: column; justify-content: flex-start; align-items: center; gap: 1.4rem; + + position: sticky; + top: 0; + + max-height: 100vh; + height: 100vh; + width: fit-content; + padding: 1rem 0.4rem; - background-color: colors.getColor("primary", 90); - border-right: 1px solid colors.getColor("primary", 80); + + background-color: colors.getColor('primary', 90); + border-right: 1px solid colors.getColor('primary', 80); } .logo { font-size: 1.6rem; - color: colors.getColor("primary", 30); + color: colors.getColor('primary', 30); } .separator { - width: 46%; + width: 70%; height: 0.25rem; border-radius: 10rem; - background-color: colors.getColor("primary", 80); + + margin: 0; + + background-color: colors.getColor('primary', 80); + + border: none; + appearance: none; } .items { display: flex; flex-direction: column; align-items: center; - font-size: 1.5rem; + font-size: 2rem; gap: 1.5rem; } diff --git a/control-station/src/components/Sidebar/Sidebar.tsx b/control-station/src/components/Sidebar/Sidebar.tsx index c1f0457ae..18dea988d 100644 --- a/control-station/src/components/Sidebar/Sidebar.tsx +++ b/control-station/src/components/Sidebar/Sidebar.tsx @@ -1,27 +1,29 @@ -import styles from "./Sidebar.module.scss"; -import { SidebarItem, SidebarItemData } from "./SidebarItem/SidebarItem"; -import { ReactComponent as TeamLogo } from "assets/svg/team_logo.svg"; -import { Link, useLocation } from "react-router-dom"; +import styles from './Sidebar.module.scss'; +import { SidebarItem, SidebarItemData } from './SidebarItem/SidebarItem'; +import { ReactComponent as TeamLogo } from 'assets/svg/team_logo.svg'; +import { Link, useLocation } from 'react-router-dom'; type Props = { items: SidebarItemData[]; }; export const Sidebar = ({ items }: Props) => { - const location = useLocation(); return ( - <nav className={styles.sidebarWrapper}> - <Link to={"/"}> + <nav className={styles.sidebar}> + <Link to={'/'}> <TeamLogo className={styles.logo} /> </Link> - <div className={styles.separator} /> + <hr className={styles.separator} /> <div className={styles.items}> {items.map((item) => { return ( <SidebarItem key={item.path} item={item} - isActive={isInSubpath(item.path, location.pathname)} + isActive={isInSubpath( + item.path, + useLocation().pathname + )} /> ); })} @@ -31,5 +33,5 @@ export const Sidebar = ({ items }: Props) => { }; function isInSubpath(itemPath: string, currentPath: string): boolean { - return "/" + currentPath.split("/")[1] == itemPath; + return '/' + currentPath.split('/')[1] == itemPath; } diff --git a/control-station/src/components/Sidebar/SidebarItem/SidebarItem.module.scss b/control-station/src/components/Sidebar/SidebarItem/SidebarItem.module.scss index a09fe0b39..4cdad64c5 100644 --- a/control-station/src/components/Sidebar/SidebarItem/SidebarItem.module.scss +++ b/control-station/src/components/Sidebar/SidebarItem/SidebarItem.module.scss @@ -1,15 +1,13 @@ -@use "src/styles/colors"; +@use 'src/styles/colors'; + +.link { + color: colors.getColor('primary', 60); -.iconWrapper { > * { font-size: 0.6rem; } } -.link { - color: colors.getColor("primary", 60); -} - .active { - color: colors.getColor("tertiary", 60); + color: colors.getColor('tertiary', 60); } diff --git a/control-station/src/components/Sidebar/SidebarItem/SidebarItem.tsx b/control-station/src/components/Sidebar/SidebarItem/SidebarItem.tsx index 039a90804..a0c03c14f 100644 --- a/control-station/src/components/Sidebar/SidebarItem/SidebarItem.tsx +++ b/control-station/src/components/Sidebar/SidebarItem/SidebarItem.tsx @@ -1,5 +1,5 @@ -import styles from "components/Sidebar/SidebarItem/SidebarItem.module.scss"; -import { NavLink } from "react-router-dom"; +import styles from 'components/Sidebar/SidebarItem/SidebarItem.module.scss'; +import { NavLink } from 'react-router-dom'; export type SidebarItemData = { path: string; @@ -15,9 +15,9 @@ export const SidebarItem = ({ item, isActive }: Props) => { return ( <NavLink to={item.path} - className={`${styles.link} ${isActive ? styles.active : ""}`} + className={`${styles.link} ${isActive && styles.active}`} > - <div className={styles.iconWrapper}> {item.icon}</div> + {item.icon} </NavLink> ); }; diff --git a/control-station/src/components/StateIndicator/StateIndicator.module.scss b/control-station/src/components/StateIndicator/StateIndicator.module.scss index 5f479b916..73de70e1d 100644 --- a/control-station/src/components/StateIndicator/StateIndicator.module.scss +++ b/control-station/src/components/StateIndicator/StateIndicator.module.scss @@ -1,13 +1,24 @@ -.wrapper { +.state_indicator { display: flex; justify-content: space-between; align-items: center; - padding: 0 .7rem; + padding: 0 0.8rem; width: 100%; - height: 1.4rem; + min-height: 1.8rem; +} + +.title { + font-family: Roboto; + font-size: 20px; + font-style: normal; + font-weight: 400; + opacity: 0.8; + margin: 0; } .icon { + opacity: 0.6; + max-width: 0.7rem; display: flex; align-items: center; -} \ No newline at end of file +} diff --git a/control-station/src/components/StateIndicator/StateIndicator.tsx b/control-station/src/components/StateIndicator/StateIndicator.tsx index b571884ba..7192b589a 100644 --- a/control-station/src/components/StateIndicator/StateIndicator.tsx +++ b/control-station/src/components/StateIndicator/StateIndicator.tsx @@ -1,41 +1,41 @@ -import { EnumMeasurement, getEnumMeasurement, NumericMeasurement, useGlobalTicker, useMeasurementsStore } from "common"; -import styles from "./StateIndicator.module.scss" -import { getStateFromEnum, State, stateToColorBackground } from "state"; -import { memo, useEffect, useRef, useState } from "react"; +import { useGlobalTicker, useMeasurementsStore } from 'common'; +import styles from './StateIndicator.module.scss'; +import { State, getState, stateToColor } from 'state'; +import { ReactNode, memo, useEffect, useRef, useState } from 'react'; interface Props { measurementId: string; - icon?: string; + icon: string; } -export const StateIndicator = memo(({measurementId, icon}: Props) => { - const getMeasurement = useMeasurementsStore(state => state.getMeasurement) - const [measurement, setMeasurement] = useState<EnumMeasurement>(getMeasurement(measurementId) as EnumMeasurement) - const state = useRef<State>(getStateFromEnum(measurement as EnumMeasurement)) +export const StateIndicator = memo(({ measurementId, icon }: Props) => { + const [measurement, measurementInfo] = useMeasurementsStore((state) => [ + state.getMeasurement(measurementId), + state.getEnumMeasurementInfo(measurementId), + ]); + + const state = useRef<State>(getState(measurement)); + + const [variant, setVariant] = useState(measurementInfo.getUpdate()); useGlobalTicker(() => { - setMeasurement(getMeasurement(measurementId) as EnumMeasurement) - }) + setVariant(measurementInfo.getUpdate()); + }); useEffect(() => { - state.current = getStateFromEnum(measurement as EnumMeasurement) - }) + state.current = getState(measurement); + }); return ( - <div className={styles.wrapper} - style={{backgroundColor: stateToColorBackground[state.current]}} + <div + className={styles.state_indicator} + style={{ backgroundColor: stateToColor[state.current] }} > - <div className={styles.icon}> - <img src={icon} alt="State icon" /> - </div> - - <div className={styles.title}> - {/* {measurement.type} */} - </div> - - <div className={styles.icon}> - <img src={icon} alt="State icon" /> - </div> + <img className={styles.icon} src={icon} alt="State icon" /> + + <p className={styles.title}>{variant}</p> + + <img className={styles.icon} src={icon} alt="State icon" /> </div> - ) -}) + ); +}); diff --git a/control-station/src/components/Window/Window.module.scss b/control-station/src/components/Window/Window.module.scss index 5192bb1d4..1d7448470 100644 --- a/control-station/src/components/Window/Window.module.scss +++ b/control-station/src/components/Window/Window.module.scss @@ -1,29 +1,27 @@ -@use "src/styles/colors"; -@use "src/styles/fonts"; +@use 'src/styles/colors'; +@use 'src/styles/fonts'; .window { display: flex; - flex-direction: column; + flex-flow: column; border-radius: 0.8rem; - overflow: hidden; filter: var(--shadow); - overflow: scroll; + overflow: hidden; } .header { - background-color: colors.getColor("tertiary", 90); - color: colors.getColor("tertiary", 60); + background-color: colors.getColor('tertiary', 90); + color: colors.getColor('tertiary', 60); font-weight: bold; - padding: .3rem .6rem; + padding: 0.3rem 0.6rem; font-size: map-get($map: fonts.$font-sizes, $key: x-small); } .content { - min-width: fit-content; display: flex; - flex: 1; justify-content: center; - padding: .6rem; - background-color: colors.getColor("primary", 99); + padding: 0.6rem; + background-color: colors.getColor('primary', 99); overflow: scroll; + height: 100%; } diff --git a/control-station/src/components/Window/Window.tsx b/control-station/src/components/Window/Window.tsx index 2b1f5b525..623479905 100644 --- a/control-station/src/components/Window/Window.tsx +++ b/control-station/src/components/Window/Window.tsx @@ -1,33 +1,16 @@ -import styles from "components/Window/Window.module.scss"; +import styles from 'components/Window/Window.module.scss'; type Props = { title: string; - height?: "fit" | "fill"; children?: React.ReactNode; + className?: string; }; -export const Window = ({ title, height = "fit", children }: Props) => { +export const Window = ({ title, children, className }: Props) => { return ( - <article - className={styles.window} - style={{ - height: - height == "fit" - ? "fit-content" - : height == "fill" - ? "100%" - : "", - }} - > + <article className={`${styles.window} ${className}`}> <header className={styles.header}>{title}</header> - <div - className={styles.content} - style={{ - flexGrow: height == "fill" ? "1" : "", - }} - > - {children} - </div> + <div className={styles.content}>{children}</div> </article> ); }; diff --git a/control-station/src/hooks/useEmergencyOrders.ts b/control-station/src/hooks/useEmergencyOrders.ts new file mode 100644 index 000000000..f87b887be --- /dev/null +++ b/control-station/src/hooks/useEmergencyOrders.ts @@ -0,0 +1,21 @@ +import { Order, useListenKey, useSendOrder } from 'common'; +import { + BrakeOrder, + OpenContactorsOrder, +} from 'pages/VehiclePage/Data2Page/hardcodedOrders'; + +export function useEmergencyOrders( + shortcut: string = ' ', + orders: Order[] = [BrakeOrder, OpenContactorsOrder] +) { + const sendOrder = useSendOrder(); + useListenKey( + shortcut, + () => { + for (const order of orders) { + sendOrder(order); + } + }, + true + ); +} diff --git a/control-station/src/hooks/usePodDataUpdate.ts b/control-station/src/hooks/usePodDataUpdate.ts new file mode 100644 index 000000000..61ef8bc51 --- /dev/null +++ b/control-station/src/hooks/usePodDataUpdate.ts @@ -0,0 +1,13 @@ +import { useMeasurementsStore, usePodDataStore, useSubscribe } from 'common'; + +export function usePodDataUpdate() { + const updatePodData = usePodDataStore((state) => state.updatePodData); + const updateMeasurements = useMeasurementsStore( + (state) => state.updateMeasurements + ); + + useSubscribe('podData/update', (update) => { + updatePodData(update); + updateMeasurements(update); + }); +} diff --git a/control-station/src/main.tsx b/control-station/src/main.tsx index 6dbbe1414..eeaab8634 100644 --- a/control-station/src/main.tsx +++ b/control-station/src/main.tsx @@ -1,25 +1,25 @@ -import "common/dist/style.css"; -import React from "react"; -import ReactDOM from "react-dom/client"; -import { Provider } from "react-redux"; +import 'common/dist/style.css'; +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { Provider } from 'react-redux'; import { createBrowserRouter, Navigate, RouterProvider, -} from "react-router-dom"; -import { App } from "./App"; -import "./index.css"; -import { vehicleRoute } from "pages/VehiclePage/vehicleRoute"; -import { camerasRoute } from "pages/CamerasPage/camerasRoute"; -import { tubeRoute } from "pages/TubePage/tubeRoute"; -import { ConfigProvider, GlobalTicker } from "common"; +} from 'react-router-dom'; +import { App } from './App'; +import './index.css'; +import { vehicleRoute } from 'pages/VehiclePage/vehicleRoute'; +import { camerasRoute } from 'pages/CamerasPage/camerasRoute'; +import { tubeRoute } from 'pages/TubePage/tubeRoute'; +import { ConfigProvider, GlobalTicker } from 'common'; const router = createBrowserRouter([ { - path: "/", + path: '/', element: <App />, children: [ - { path: "", element: <Navigate to={"vehicle"} /> }, + { path: '', element: <Navigate to={'vehicle'} /> }, vehicleRoute, camerasRoute, tubeRoute, @@ -27,13 +27,10 @@ const router = createBrowserRouter([ }, ]); -ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( +ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( <React.StrictMode> - <ConfigProvider - devIp="127.0.0.1" - prodIp="127.0.0.1" - > - <GlobalTicker fps={60}> + <ConfigProvider devIp="127.0.0.1" prodIp="127.0.0.1"> + <GlobalTicker fps={30}> <RouterProvider router={router}></RouterProvider> </GlobalTicker> </ConfigProvider> diff --git a/control-station/src/pages/CamerasPage/useOneDofData.ts b/control-station/src/pages/CamerasPage/useOneDofData.ts deleted file mode 100644 index 3e13283ec..000000000 --- a/control-station/src/pages/CamerasPage/useOneDofData.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { NumericMeasurement } from "common"; -import { useMeasurements } from "pages/VehiclePage/useMeasurements"; - -type OneDofData = { - y: number; - rotX: number; - rotY: number; - rotZ: number; -}; - -export function useOneDofData(): OneDofData { - const measurements = useMeasurements(); - - return { - rotX: - ( - measurements.measurements[ - "LCU_MASTER/rot_x" - ] as NumericMeasurement - ).value.last ?? 0, - rotY: - ( - measurements.measurements[ - "LCU_MASTER/rot_y" - ] as NumericMeasurement - ).value.last ?? 0, - rotZ: - ( - measurements.measurements[ - "LCU_MASTER/rot_z" - ] as NumericMeasurement - ).value.last ?? 0, - y: - (measurements.measurements["LCU_MASTER/y"] as NumericMeasurement) - .value.last ?? 0, - }; -} diff --git a/control-station/src/pages/PageWrapper/PageWrapper.module.scss b/control-station/src/pages/PageWrapper/PageWrapper.module.scss index 275889043..ba565d0b0 100644 --- a/control-station/src/pages/PageWrapper/PageWrapper.module.scss +++ b/control-station/src/pages/PageWrapper/PageWrapper.module.scss @@ -1,12 +1,14 @@ -@use "src/styles/colors"; -@use "src/styles/fonts"; +@use 'src/styles/colors'; +@use 'src/styles/fonts'; -.pageWrapper { +.page { display: flex; - flex: 1; - flex-direction: column; - gap: .6rem; - padding: .8rem; + flex-flow: column; + gap: 0.6rem; + + width: 100%; + + padding: 0.8rem; } .header { @@ -14,24 +16,26 @@ flex-direction: row; align-items: center; + flex-grow: 0; + flex-shrink: 0; + > h1 { font-size: map-get($map: fonts.$font-sizes, $key: default); } -} -.header::after { - content: " "; - width: 100%; - height: 3px; - margin-left: 1.3rem; - background-color: colors.getColor("primary", 90); - border-radius: 1rem; + &::after { + content: ''; + + width: 100%; + height: 3px; + border-radius: 1rem; + + margin-left: 1.3rem; + + background-color: colors.getColor('primary', 90); + } } .content { - display: flex; - flex-direction: column; - align-items: center; - gap: .6rem; - height: 100%; + width: 100%; } diff --git a/control-station/src/pages/PageWrapper/PageWrapper.tsx b/control-station/src/pages/PageWrapper/PageWrapper.tsx index 41d06fe4a..904325f1d 100644 --- a/control-station/src/pages/PageWrapper/PageWrapper.tsx +++ b/control-station/src/pages/PageWrapper/PageWrapper.tsx @@ -1,4 +1,4 @@ -import styles from "pages/PageWrapper/PageWrapper.module.scss"; +import styles from 'pages/PageWrapper/PageWrapper.module.scss'; type Props = { title: string; @@ -7,7 +7,7 @@ type Props = { export const PageWrapper = ({ title, children }: Props) => { return ( - <main className={styles.pageWrapper}> + <main className={styles.page}> <header className={styles.header}> <h1>{title}</h1> </header> diff --git a/control-station/src/pages/TubePage/TubeData/TubeData.tsx b/control-station/src/pages/TubePage/TubeData/TubeData.tsx index d51124a65..15f6c44ec 100644 --- a/control-station/src/pages/TubePage/TubeData/TubeData.tsx +++ b/control-station/src/pages/TubePage/TubeData/TubeData.tsx @@ -1,15 +1,14 @@ -import { GaugeTag } from "components/GaugeTag/GaugeTag"; -import { PumpIndicator } from "./PumpIndicator/PumpIndicator"; -import styles from "./TubeData.module.scss"; -import { NumericMeasurement } from "common"; +import { PumpIndicator } from './PumpIndicator/PumpIndicator'; +import styles from './TubeData.module.scss'; +import { NumericMeasurement } from 'common'; const defaultMeasurement: NumericMeasurement = { - id: "test", - name: "test", + id: 'test', + name: 'test', safeRange: [0, 100], - type: "int16", - units: "A", - value: { average: 10, last: 12 }, + type: 'int16', + units: 'A', + value: { average: 10, last: 12, showLatest: true }, warningRange: [0, 100], }; @@ -18,21 +17,7 @@ const GAUGE_WIDTH = 130; export const TubeData = () => { return ( <div className={styles.tubeDataWrapper}> - <GaugeTag - className="" - measurement={defaultMeasurement} - min={defaultMeasurement.safeRange[0] ?? 0} - max={defaultMeasurement.safeRange[1] ?? 100} - strokeWidth={GAUGE_WIDTH} - ></GaugeTag> <PumpIndicator isOn={true} /> - <GaugeTag - className="" - measurement={defaultMeasurement} - min={defaultMeasurement.safeRange[0] ?? 0} - max={defaultMeasurement.safeRange[1] ?? 100} - strokeWidth={GAUGE_WIDTH} - ></GaugeTag> </div> ); }; diff --git a/control-station/src/pages/VehiclePage/Boards/BCU/BCU.module.scss b/control-station/src/pages/VehiclePage/Boards/BCU/BCU.module.scss new file mode 100644 index 000000000..0dac3bbc7 --- /dev/null +++ b/control-station/src/pages/VehiclePage/Boards/BCU/BCU.module.scss @@ -0,0 +1,43 @@ +.container { + display: flex; + flex-flow: column; + max-width: 670px; +} +.content { + display: flex; + flex-flow: column; + justify-content: center; + align-items: center; + gap: 1rem; + width: 100%; + margin: 1rem 0; +} + +.current_display { + display: flex; + flex-flow: row; + align-items: center; + justify-content: center; + width: 100%; +} + +.current_chart { + display: flex; + flex-flow: column; + align-items: center; + justify-content: center; + gap: 5px; + width: 100%; +} + +.chart_title { + margin: 0; +} + +.chart { + width: 100%; +} + +.frequency { + max-width: 15rem; +} diff --git a/control-station/src/pages/VehiclePage/Boards/BCU/BCU.tsx b/control-station/src/pages/VehiclePage/Boards/BCU/BCU.tsx new file mode 100644 index 000000000..53001a4ca --- /dev/null +++ b/control-station/src/pages/VehiclePage/Boards/BCU/BCU.tsx @@ -0,0 +1,173 @@ +import { Window } from 'components/Window/Window'; +import { ReactComponent as LSMIcon } from '../../../../assets/svg/lsm.svg'; +import styles from './BCU.module.scss'; +import { ColorfulChart, BcuMeasurements, useMeasurementsStore } from 'common'; +import { IndicatorStack } from 'components/IndicatorStack/IndicatorStack'; +import { BarIndicator } from 'components/BarIndicator/BarIndicator'; +import thunderIcon from 'assets/svg/thunder-filled.svg'; + +export const BCU = () => { + const getNumericMeasurementInfo = useMeasurementsStore( + (state) => state.getNumericMeasurementInfo + ); + + const bpu1CurrentU = getNumericMeasurementInfo( + BcuMeasurements.bpu1CurrentU + ); + const bpu1CurrentV = getNumericMeasurementInfo( + BcuMeasurements.bpu1CurrentV + ); + const bpu1CurrentW = getNumericMeasurementInfo( + BcuMeasurements.bpu1CurrentW + ); + + const bpu2CurrentU = getNumericMeasurementInfo( + BcuMeasurements.bpu2CurrentU + ); + const bpu2CurrentV = getNumericMeasurementInfo( + BcuMeasurements.bpu2CurrentV + ); + const bpu2CurrentW = getNumericMeasurementInfo( + BcuMeasurements.bpu2CurrentW + ); + + return ( + <Window title="BCU"> + <div className={styles.container}> + <LSMIcon /> + + <div className={styles.content}> + <div className={styles.current_display}> + <div className={styles.current_chart}> + <p className={styles.chart_title}>BPU 1</p> + <ColorfulChart + className={styles.chart} + length={35} + items={[ + bpu1CurrentU, + bpu1CurrentV, + bpu1CurrentW, + ]} + /> + <IndicatorStack> + <BarIndicator + title="Current U" + icon={thunderIcon} + getValue={bpu1CurrentU.getUpdate} + safeRangeMin={bpu1CurrentU.range[0]!!} + safeRangeMax={bpu1CurrentU.range[1]!!} + warningRangeMin={ + bpu1CurrentU.warningRange[0]!! + } + warningRangeMax={ + bpu1CurrentU.warningRange[1]!! + } + units={bpu1CurrentU.units} + color="#EE8735" + backgroundColor="#FFE7CF" + /> + <BarIndicator + title="Current V" + icon={thunderIcon} + getValue={bpu1CurrentV.getUpdate} + safeRangeMin={bpu1CurrentV.range[0]!!} + safeRangeMax={bpu1CurrentV.range[1]!!} + warningRangeMin={ + bpu1CurrentV.warningRange[0]!! + } + warningRangeMax={ + bpu1CurrentV.warningRange[1]!! + } + units={bpu1CurrentV.units} + color="#51C6EB" + backgroundColor="#CEF3FF" + /> + <BarIndicator + title="Current W" + icon={thunderIcon} + getValue={bpu1CurrentW.getUpdate} + safeRangeMin={bpu1CurrentW.range[0]!!} + safeRangeMax={bpu1CurrentW.range[1]!!} + warningRangeMin={ + bpu1CurrentW.warningRange[0]!! + } + warningRangeMax={ + bpu1CurrentW.warningRange[1]!! + } + units={bpu1CurrentW.units} + color="#7BEE35" + backgroundColor="#E5FFD4" + /> + </IndicatorStack> + </div> + + <div className={styles.current_chart}> + <p className={styles.chart_title}>BPU 2</p> + <ColorfulChart + className={styles.chart} + length={35} + items={[ + bpu2CurrentU, + bpu2CurrentV, + bpu2CurrentW, + ]} + /> + <IndicatorStack> + <BarIndicator + title="Current U" + icon={thunderIcon} + getValue={bpu2CurrentU.getUpdate} + safeRangeMin={bpu2CurrentU.range[0]!!} + safeRangeMax={bpu2CurrentU.range[1]!!} + warningRangeMin={ + bpu2CurrentU.warningRange[0]!! + } + warningRangeMax={ + bpu2CurrentU.warningRange[1]!! + } + units="A" + color="#EE8735" + backgroundColor="#FFE7CF" + /> + <BarIndicator + title="Current V" + icon={thunderIcon} + getValue={bpu2CurrentV.getUpdate} + safeRangeMin={bpu2CurrentV.range[0]!!} + safeRangeMax={bpu2CurrentV.range[1]!!} + warningRangeMin={ + bpu2CurrentV.warningRange[0]!! + } + warningRangeMax={ + bpu2CurrentV.warningRange[1]!! + } + units="A" + color="#51C6EB" + backgroundColor="#CEF3FF" + /> + <BarIndicator + title="Current W" + icon={thunderIcon} + getValue={bpu2CurrentW.getUpdate} + safeRangeMin={bpu2CurrentW.range[0]!!} + safeRangeMax={bpu2CurrentW.range[1]!!} + warningRangeMin={ + bpu2CurrentW.warningRange[0]!! + } + warningRangeMax={ + bpu2CurrentW.warningRange[1]!! + } + units="A" + color="#7BEE35" + backgroundColor="#E5FFD4" + /> + </IndicatorStack> + </div> + </div> + </div> + + <LSMIcon style={{ transform: 'rotate(180deg)' }} /> + </div> + </Window> + ); +}; diff --git a/control-station/src/pages/VehiclePage/Boards/BMSL/BMSL.module.scss b/control-station/src/pages/VehiclePage/Boards/BMSL/BMSL.module.scss deleted file mode 100644 index ac34dd804..000000000 --- a/control-station/src/pages/VehiclePage/Boards/BMSL/BMSL.module.scss +++ /dev/null @@ -1,18 +0,0 @@ -.bmsl { - display: flex; - flex-direction: column; - gap: 1rem; -} - -.row { - display: flex; - justify-content: center; - gap: 1rem; -} - -.column { - display: flex; - flex-direction: column; - justify-content: center; - gap: 1rem; -} \ No newline at end of file diff --git a/control-station/src/pages/VehiclePage/Boards/BMSL/BMSL.tsx b/control-station/src/pages/VehiclePage/Boards/BMSL/BMSL.tsx index 48f42a8db..0fe740a04 100644 --- a/control-station/src/pages/VehiclePage/Boards/BMSL/BMSL.tsx +++ b/control-station/src/pages/VehiclePage/Boards/BMSL/BMSL.tsx @@ -1,175 +1,156 @@ -import styles from "./BMSL.module.scss"; -import { Window } from "components/Window/Window"; -import { BmslMeasurements, useMeasurementsStore } from "common"; -import { GaugeTag } from "components/GaugeTag/GaugeTag"; -import { BarIndicator } from "components/BarIndicator/BarIndicator"; -import { IndicatorStack } from "components/IndicatorStack/IndicatorStack"; -import thermometerIcon from "assets/svg/thermometer-filled.svg"; -import batteryIcon from "assets/svg/battery-filled.svg"; -import thunderIcon from "assets/svg/thunder-filled.svg"; +import { Window } from 'components/Window/Window'; +import { GaugeTag } from 'components/GaugeTag/GaugeTag'; +import { BmslMeasurements, useMeasurementsStore } from 'common'; +import { IndicatorStack } from 'components/IndicatorStack/IndicatorStack'; +import { BarIndicator } from 'components/BarIndicator/BarIndicator'; +import batteryIcon from 'assets/svg/battery-filled.svg'; +import thermometerIcon from 'assets/svg/thermometer-filled.svg'; export const BMSL = () => { - const getNumericMeasurementInfo = useMeasurementsStore((state) => state.getNumericMeasurementInfo); - const totalVoltageLow = getNumericMeasurementInfo(BmslMeasurements.totalVoltageLow); - const avCurrent = getNumericMeasurementInfo(BmslMeasurements.avCurrent); - const lowSOC1 = getNumericMeasurementInfo(BmslMeasurements.lowSOC1); - const lowBatteryTemperature1 = getNumericMeasurementInfo(BmslMeasurements.lowBatteryTemperature1); - const lowBatteryTemperature2 = getNumericMeasurementInfo(BmslMeasurements.lowBatteryTemperature2); - const inputChargingCurrent = getNumericMeasurementInfo(BmslMeasurements.inputChargingCurrent); - const inputChargingVoltage = getNumericMeasurementInfo(BmslMeasurements.inputChargingVoltage); - const outputChargingCurrent = getNumericMeasurementInfo(BmslMeasurements.outputChargingCurrent); - const outputChargingVoltage = getNumericMeasurementInfo(BmslMeasurements.outputChargingVoltage); - const lowCell1 = getNumericMeasurementInfo(BmslMeasurements.lowCell1); - const lowCell2 = getNumericMeasurementInfo(BmslMeasurements.lowCell2); - const lowCell3 = getNumericMeasurementInfo(BmslMeasurements.lowCell3); - const lowCell4 = getNumericMeasurementInfo(BmslMeasurements.lowCell4); - const lowCell5 = getNumericMeasurementInfo(BmslMeasurements.lowCell5); - const lowCell6 = getNumericMeasurementInfo(BmslMeasurements.lowCell6); + const getNumericMeasurementInfo = useMeasurementsStore( + (state) => state.getNumericMeasurementInfo + ); + + const cell1 = getNumericMeasurementInfo(BmslMeasurements.cell1); + const cell2 = getNumericMeasurementInfo(BmslMeasurements.cell2); + const cell3 = getNumericMeasurementInfo(BmslMeasurements.cell3); + const cell4 = getNumericMeasurementInfo(BmslMeasurements.cell4); + const cell5 = getNumericMeasurementInfo(BmslMeasurements.cell5); + const cell6 = getNumericMeasurementInfo(BmslMeasurements.cell6); + + const temp1 = getNumericMeasurementInfo(BmslMeasurements.temp1); + const temp2 = getNumericMeasurementInfo(BmslMeasurements.temp2); + + const totalVoltage = getNumericMeasurementInfo( + BmslMeasurements.totalVoltage + ); + const dischargeCurrent = getNumericMeasurementInfo( + BmslMeasurements.dischargeCurrent + ); return ( <Window title="BMSL"> - <div className={styles.bmsl}> - <div className={styles.row}> + <div + style={{ + display: 'flex', + flexFlow: 'column', + gap: '1rem', + height: '100%', + }} + > + <div + style={{ + flex: '1', + display: 'flex', + flexFlow: 'row', + gap: '.5rem', + }} + > <GaugeTag - name={totalVoltageLow.name} - units={totalVoltageLow.units} - getUpdate={totalVoltageLow.getUpdate} + id="bmsl_general_voltage" + name={'Voltage'} + units={'Volts'} + getUpdate={totalVoltage.getUpdate} strokeWidth={120} - min={totalVoltageLow.range[0] || 0} - max={totalVoltageLow.range[1] || 0} + min={totalVoltage.warningRange[0] ?? 225} + max={totalVoltage.warningRange[1] ?? 252} /> <GaugeTag - name={avCurrent.name} - units={avCurrent.units} - getUpdate={avCurrent.getUpdate} + id="bmsl_general_current" + name={'Current'} + units={'Amps'} + getUpdate={dischargeCurrent.getUpdate} strokeWidth={120} - min={avCurrent.range[1] || 0} - max={avCurrent.range[1] || 0} + min={dischargeCurrent.warningRange[0] ?? 0} + max={dischargeCurrent.warningRange[1] ?? 100} /> </div> - <div className={styles.row}> - <IndicatorStack> - <BarIndicator - title="SoC" - icon={batteryIcon} - getValue={lowSOC1.getUpdate} - safeRangeMin={lowSOC1.range[0]!!} - safeRangeMax={lowSOC1.range[1]!!} - units="%" - /> - </IndicatorStack> - <IndicatorStack> - <BarIndicator - title="Temperature 1" - icon={thermometerIcon} - getValue={lowBatteryTemperature1.getUpdate} - safeRangeMin={lowBatteryTemperature1.range[0]!!} - safeRangeMax={lowBatteryTemperature1.range[1]!!} - units="ºC" - /> - <BarIndicator - title="Temperature 2" - icon={thermometerIcon} - getValue={lowBatteryTemperature2.getUpdate} - safeRangeMin={lowBatteryTemperature2.range[0]!!} - safeRangeMax={lowBatteryTemperature2.range[1]!!} - units="ºC" - /> - </IndicatorStack> - </div> - <div className={styles.row}> - <div className={styles.column}> - <IndicatorStack> - <BarIndicator - title="Input Current" - icon={thunderIcon} - getValue={inputChargingCurrent.getUpdate} - safeRangeMin={inputChargingCurrent.range[0]!!} - safeRangeMax={inputChargingCurrent.range[1]!!} - units="A" - /> - <BarIndicator - title="Input Voltage" - icon={thunderIcon} - getValue={inputChargingVoltage.getUpdate} - safeRangeMin={inputChargingVoltage.range[0]!!} - safeRangeMax={inputChargingVoltage.range[1]!!} - units="V" - /> - </IndicatorStack> - <IndicatorStack> - <BarIndicator - title="Output Current" - icon={thunderIcon} - getValue={outputChargingCurrent.getUpdate} - safeRangeMin={outputChargingCurrent.range[0]!!} - safeRangeMax={outputChargingCurrent.range[1]!!} - units="A" - /> - <BarIndicator - title="Output Voltage" - icon={thunderIcon} - getValue={outputChargingVoltage.getUpdate} - safeRangeMin={outputChargingVoltage.range[0]!!} - safeRangeMax={outputChargingVoltage.range[1]!!} - units="V" - /> - </IndicatorStack> - </div> - <div className={styles.column}> - <IndicatorStack> - <BarIndicator - title="Cell 1" - icon={batteryIcon} - getValue={lowCell1.getUpdate} - safeRangeMin={lowCell1.range[0]!!} - safeRangeMax={lowCell1.range[1]!!} - units="V" - /> - <BarIndicator - title="Cell 2" - icon={batteryIcon} - getValue={lowCell2.getUpdate} - safeRangeMin={lowCell2.range[0]!!} - safeRangeMax={lowCell2.range[1]!!} - units="V" - /> - <BarIndicator - title="Cell 3" - icon={batteryIcon} - getValue={lowCell3.getUpdate} - safeRangeMin={lowCell3.range[0]!!} - safeRangeMax={lowCell3.range[1]!!} - units="V" - /> - <BarIndicator - title="Cell 4" - icon={batteryIcon} - getValue={lowCell4.getUpdate} - safeRangeMin={lowCell4.range[0]!!} - safeRangeMax={lowCell4.range[1]!!} - units="V" - /> - <BarIndicator - title="Cell 5" - icon={batteryIcon} - getValue={lowCell5.getUpdate} - safeRangeMin={lowCell5.range[0]!!} - safeRangeMax={lowCell5.range[1]!!} - units="V" - /> - <BarIndicator - title="Cell 6" - icon={batteryIcon} - getValue={lowCell6.getUpdate} - safeRangeMin={lowCell6.range[0]!!} - safeRangeMax={lowCell6.range[1]!!} - units="V" - /> - </IndicatorStack> - </div> - </div> + <IndicatorStack> + <BarIndicator + icon={batteryIcon} + title="Cell 1" + getValue={cell1.getUpdate} + safeRangeMin={cell1.range[0]!!} + safeRangeMax={cell1.range[1]!!} + warningRangeMin={cell1.warningRange[0]!!} + warningRangeMax={cell1.warningRange[1]!!} + units={cell1.units} + /> + <BarIndicator + icon={batteryIcon} + title="Cell 2" + getValue={cell2.getUpdate} + safeRangeMin={cell2.range[0]!!} + safeRangeMax={cell2.range[1]!!} + warningRangeMin={cell2.warningRange[0]!!} + warningRangeMax={cell2.warningRange[1]!!} + units={cell2.units} + /> + <BarIndicator + icon={batteryIcon} + title="Cell 3" + getValue={cell3.getUpdate} + safeRangeMin={cell3.range[0]!!} + safeRangeMax={cell3.range[1]!!} + warningRangeMin={cell3.warningRange[0]!!} + warningRangeMax={cell3.warningRange[1]!!} + units={cell3.units} + /> + <BarIndicator + icon={batteryIcon} + title="Cell 4" + getValue={cell4.getUpdate} + safeRangeMin={cell4.range[0]!!} + safeRangeMax={cell4.range[1]!!} + warningRangeMin={cell4.warningRange[0]!!} + warningRangeMax={cell4.warningRange[1]!!} + units={cell4.units} + /> + <BarIndicator + icon={batteryIcon} + title="Cell 5" + getValue={cell5.getUpdate} + safeRangeMin={cell5.range[0]!!} + safeRangeMax={cell5.range[1]!!} + warningRangeMin={cell5.warningRange[0]!!} + warningRangeMax={cell5.warningRange[1]!!} + units={cell5.units} + /> + <BarIndicator + icon={batteryIcon} + title="Cell 6" + getValue={cell6.getUpdate} + safeRangeMin={cell6.range[0]!!} + safeRangeMax={cell6.range[1]!!} + warningRangeMin={cell6.warningRange[0]!!} + warningRangeMax={cell6.warningRange[1]!!} + units={cell6.units} + /> + </IndicatorStack> + + <IndicatorStack> + <BarIndicator + icon={thermometerIcon} + title="Temp 1" + getValue={temp1.getUpdate} + safeRangeMin={temp1.range[0]!!} + safeRangeMax={temp1.range[1]!!} + warningRangeMin={temp1.warningRange[0]!!} + warningRangeMax={temp1.warningRange[1]!!} + units={temp1.units} + /> + <BarIndicator + icon={thermometerIcon} + title="Temp 2" + getValue={temp2.getUpdate} + safeRangeMin={temp2.range[0]!!} + safeRangeMax={temp2.range[1]!!} + warningRangeMin={temp2.warningRange[0]!!} + warningRangeMax={temp2.warningRange[1]!!} + units={temp2.units} + /> + </IndicatorStack> </div> </Window> ); diff --git a/control-station/src/pages/VehiclePage/Boards/DLIM/DLIM.module.scss b/control-station/src/pages/VehiclePage/Boards/DLIM/DLIM.module.scss deleted file mode 100644 index 481d4b311..000000000 --- a/control-station/src/pages/VehiclePage/Boards/DLIM/DLIM.module.scss +++ /dev/null @@ -1,6 +0,0 @@ -.DLIMWrapper { - display: flex; - flex-direction: column; - align-items: center; - gap: 1rem; -} \ No newline at end of file diff --git a/control-station/src/pages/VehiclePage/Boards/DLIM/DLIM.tsx b/control-station/src/pages/VehiclePage/Boards/DLIM/DLIM.tsx deleted file mode 100644 index 6192b9665..000000000 --- a/control-station/src/pages/VehiclePage/Boards/DLIM/DLIM.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Window } from "components/Window/Window" -import styles from "./DLIM.module.scss" -import dlim from "assets/svg/DLIM.svg" - -export const DLIM = () => { - return ( - <Window title="DLIM"> - <div className={styles.DLIMWrapper}> - <img - src={dlim} - alt="DLIM" - /> - - <img - src={dlim} - style={{ - rotate: "180deg" - }} - alt="DLIM" - /> - </div> - </Window> - ) -} diff --git a/control-station/src/pages/VehiclePage/Boards/LCU/LCU.tsx b/control-station/src/pages/VehiclePage/Boards/LCU/LCU.tsx index edec08c2a..604a947a8 100644 --- a/control-station/src/pages/VehiclePage/Boards/LCU/LCU.tsx +++ b/control-station/src/pages/VehiclePage/Boards/LCU/LCU.tsx @@ -1,66 +1,82 @@ -import { Window } from "components/Window/Window" -import styles from "./LCU.module.scss" -import { LevitationUnit } from "components/LevitationUnit/LevitationUnit" -import ems from "assets/svg/ems.svg" -import hems from "assets/svg/hems.svg" -import { BarIndicator } from "components/BarIndicator/BarIndicator" -import pitchRotation from "assets/svg/pitch-rotation.svg" -import yawRotation from "assets/svg/yaw-rotation.svg" -import rollRotation from "assets/svg/roll-rotation.svg" -import zIndex from "assets/svg/z-index.svg" -import yIndex from "assets/svg/y-index.svg" -import { IndicatorStack } from "components/IndicatorStack/IndicatorStack" +import { Window } from 'components/Window/Window'; +import styles from './LCU.module.scss'; +import { BarIndicator } from 'components/BarIndicator/BarIndicator'; +import pitchRotation from 'assets/svg/pitch-rotation.svg'; +import yawRotation from 'assets/svg/yaw-rotation.svg'; +import rollRotation from 'assets/svg/roll-rotation.svg'; +import zIndex from 'assets/svg/z-index.svg'; +import yIndex from 'assets/svg/y-index.svg'; +import { IndicatorStack } from 'components/IndicatorStack/IndicatorStack'; +import { LevitationUnit } from 'components/LevitationUnit/LevitationUnit'; +import { LcuMeasurements, useMeasurementsStore } from 'common'; export const LCU = () => { + const getNumericMeasurementInfo = useMeasurementsStore( + (state) => state.getNumericMeasurementInfo + ); + + const pitch = getNumericMeasurementInfo(LcuMeasurements.rotationPitch); + const roll = getNumericMeasurementInfo(LcuMeasurements.rotationRoll); + const yaw = getNumericMeasurementInfo(LcuMeasurements.rotationYaw); + const positionY = getNumericMeasurementInfo(LcuMeasurements.positionY); + const positionZ = getNumericMeasurementInfo(LcuMeasurements.positionZ); + return ( <Window title="LCU"> <div className={styles.LCUWrapper}> <div className={styles.levitationUnitsWrapper}> <div className={styles.levitationUnitsColumn}> - <LevitationUnit - imgSrc={hems} + <LevitationUnit + unitIndex={0} + kind="hems" imageSide="left" /> - <LevitationUnit - imgSrc={ems} + <LevitationUnit + unitIndex={4} + kind="ems" imageSide="left" /> - <LevitationUnit - imgSrc={ems} + <LevitationUnit + unitIndex={6} + kind="ems" imageSide="left" /> - <LevitationUnit - imgSrc={ems} + <LevitationUnit + unitIndex={8} + kind="ems" imageSide="left" /> - <LevitationUnit - imgSrc={hems} + <LevitationUnit + unitIndex={2} + kind="hems" imageSide="left" /> </div> <div className={styles.levitationUnitsColumn}> - <LevitationUnit - imgSrc={hems} + <LevitationUnit + unitIndex={1} + kind="hems" imageSide="right" /> - <LevitationUnit - imgSrc={ems} + <LevitationUnit + unitIndex={5} + kind="ems" imageSide="right" - rotate /> - <LevitationUnit - imgSrc={ems} + <LevitationUnit + unitIndex={7} + kind="ems" imageSide="right" - rotate /> - <LevitationUnit - imgSrc={ems} + <LevitationUnit + unitIndex={9} + kind="ems" imageSide="right" - rotate /> - <LevitationUnit - imgSrc={hems} + <LevitationUnit + unitIndex={3} + kind="hems" imageSide="right" /> </div> @@ -68,53 +84,67 @@ export const LCU = () => { <div className={styles.rotationIndicatorsWrapper}> <IndicatorStack> - <BarIndicator + <BarIndicator icon={pitchRotation} title="Pitch" - getValue={() => 0} - safeRangeMin={0} - safeRangeMax={10} + getValue={pitch.getUpdate} + safeRangeMin={pitch.range[0] ?? -0.05} + safeRangeMax={pitch.range[1] ?? 0.05} + warningRangeMin={pitch.warningRange[0] ?? -0.1} + warningRangeMax={pitch.warningRange[1] ?? 0.1} + units={roll.units} /> </IndicatorStack> <IndicatorStack> - <BarIndicator + <BarIndicator icon={rollRotation} title="Roll" - getValue={() => 0} - safeRangeMin={0} - safeRangeMax={10} + getValue={roll.getUpdate} + safeRangeMin={roll.range[0] ?? -0.05} + safeRangeMax={roll.range[1] ?? 0.05} + warningRangeMin={roll.warningRange[0] ?? -0.1} + warningRangeMax={roll.warningRange[1] ?? 0.1} + units={roll.units} /> </IndicatorStack> <IndicatorStack> - <BarIndicator + <BarIndicator icon={yawRotation} title="Yaw" - getValue={() => 0} - safeRangeMin={0} - safeRangeMax={10} + getValue={yaw.getUpdate} + safeRangeMin={yaw.range[0] ?? -0.005} + safeRangeMax={yaw.range[1] ?? 0.005} + warningRangeMin={yaw.warningRange[0] ?? -0.01} + warningRangeMax={yaw.warningRange[1] ?? 0.01} + units={yaw.units} /> </IndicatorStack> <IndicatorStack> - <BarIndicator + <BarIndicator icon={zIndex} title="Z" - getValue={() => 0} - safeRangeMin={0} - safeRangeMax={10} + getValue={positionZ.getUpdate} + safeRangeMin={positionZ.range[0] ?? -2} + safeRangeMax={positionZ.range[1] ?? 2} + warningRangeMin={positionZ.warningRange[0] ?? -10} + warningRangeMax={positionZ.warningRange[1] ?? 10} + units={positionZ.units} /> </IndicatorStack> <IndicatorStack> - <BarIndicator + <BarIndicator icon={yIndex} title="Y" - getValue={() => 0} - safeRangeMin={0} - safeRangeMax={10} + getValue={positionY.getUpdate} + safeRangeMin={positionY.range[0] ?? -5} + safeRangeMax={positionY.range[1] ?? 5} + warningRangeMin={positionY.warningRange[0] ?? -10} + warningRangeMax={positionY.warningRange[1] ?? 10} + units={positionY.units} /> </IndicatorStack> - </div> </div> </Window> - ) -} + ); +}; diff --git a/control-station/src/pages/VehiclePage/Boards/LSM/LSM.tsx b/control-station/src/pages/VehiclePage/Boards/LSM/LSM.tsx deleted file mode 100644 index 6d033d344..000000000 --- a/control-station/src/pages/VehiclePage/Boards/LSM/LSM.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Window } from "components/Window/Window"; -import styles from "./LSM.module.scss"; -import lsm from "assets/svg/LSM.svg"; - -export const LSM = () => { - return ( - <Window title="LSM"> - <div className={styles.LSMWrapper}> - <img - src={lsm} - alt="LSM" - /> - - <img - src={lsm} - style={{ - rotate: "180deg" - }} - alt="LSM" - /> - </div> - </Window> - ) -} diff --git a/control-station/src/pages/VehiclePage/Boards/OBCCU/BatteryPack/BatteryPack.module.scss b/control-station/src/pages/VehiclePage/Boards/OBCCU/BatteryPack/BatteryPack.module.scss index f41450f21..9cf43592c 100644 --- a/control-station/src/pages/VehiclePage/Boards/OBCCU/BatteryPack/BatteryPack.module.scss +++ b/control-station/src/pages/VehiclePage/Boards/OBCCU/BatteryPack/BatteryPack.module.scss @@ -1,18 +1,9 @@ -.container { - display: flex; - width: 100%; - flex-direction: column; - align-items: center; - justify-content: center; - border-radius: 1rem; - overflow: hidden; - margin: .1rem 0; +.balancing { + box-shadow: 0px 0px 15px 0px rgba(26, 90, 255, 0.7); + border: var(--Stroke-M, 2px) solid #1a5aff; +} - > div { - border-bottom: 1.5px solid hsla(0, 0%, 0%, 0.792); - } - - > div:last-child, > div:nth-last-child(2):nth-child(odd) { - border-bottom: none; - } -} \ No newline at end of file +.idle { + box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, 0); + border: var(--Stroke-M, 2px) solid rgba(0, 0, 0, 0); +} diff --git a/control-station/src/pages/VehiclePage/Boards/OBCCU/BatteryPack/BatteryPack.tsx b/control-station/src/pages/VehiclePage/Boards/OBCCU/BatteryPack/BatteryPack.tsx index c3d9d4472..fdc48b584 100644 --- a/control-station/src/pages/VehiclePage/Boards/OBCCU/BatteryPack/BatteryPack.tsx +++ b/control-station/src/pages/VehiclePage/Boards/OBCCU/BatteryPack/BatteryPack.tsx @@ -1,10 +1,11 @@ -import { BarIndicator } from 'components/BarIndicator/BarIndicator' -import batteryIcon from "assets/svg/battery-filled.svg" -import thermometerIcon from "assets/svg/thermometer-filled.svg" -import thunderIcon from "assets/svg/thunder-filled.svg" -import { useMeasurementsStore } from 'common' -import { memo } from 'react' -import { IndicatorStack } from 'components/IndicatorStack/IndicatorStack' +import { BarIndicator } from 'components/BarIndicator/BarIndicator'; +import batteryIcon from 'assets/svg/battery-filled.svg'; +import thermometerIcon from 'assets/svg/thermometer-filled.svg'; +import thunderIcon from 'assets/svg/thunder-filled.svg'; +import { useGlobalTicker, useMeasurementsStore } from 'common'; +import { memo, useState } from 'react'; +import { IndicatorStack } from 'components/IndicatorStack/IndicatorStack'; +import styles from './BatteryPack.module.scss'; interface Props { stateOfChargeMeasurementId: string; @@ -12,67 +13,101 @@ interface Props { maxCellMeasurementId: string; minCellMeasurementId: string; voltageMeasurementId: string; + isBalancingMeasurementId: string; } -export const BatteryPack = memo(( - { +export const BatteryPack = memo( + ({ stateOfChargeMeasurementId, temperatureMeasurementId, maxCellMeasurementId, minCellMeasurementId, - voltageMeasurementId - }: Props -) => { - - const getNumericMeasurementInfo = useMeasurementsStore(state => state.getNumericMeasurementInfo) - const stateOfChargeMeasurement = getNumericMeasurementInfo(stateOfChargeMeasurementId) - const temperatureMeasurement = getNumericMeasurementInfo(temperatureMeasurementId) - const maxCellMeasurement = getNumericMeasurementInfo(maxCellMeasurementId) - const minCellMeasurement = getNumericMeasurementInfo(minCellMeasurementId) - const voltageMeasurement = getNumericMeasurementInfo(voltageMeasurementId) + voltageMeasurementId, + isBalancingMeasurementId, + }: Props) => { + const getNumericMeasurementInfo = useMeasurementsStore( + (state) => state.getNumericMeasurementInfo + ); + const getBooleanMeasurementInfo = useMeasurementsStore( + (state) => state.getBooleanMeasurementInfo + ); - return ( - <IndicatorStack> - <BarIndicator - icon={batteryIcon} - title="SoC" - getValue = {stateOfChargeMeasurement.getUpdate} - safeRangeMin={stateOfChargeMeasurement.range[0]!!} - safeRangeMax={stateOfChargeMeasurement.range[1]!!} - units="%" - /> - <BarIndicator - icon={thermometerIcon} - title="Temperature" - getValue = {temperatureMeasurement.getUpdate} - safeRangeMin={temperatureMeasurement.range[0]!!} - safeRangeMax={temperatureMeasurement.range[1]!!} - units="ºC" - /> - <BarIndicator - icon={thunderIcon} - title="Max Cell" - getValue = {maxCellMeasurement.getUpdate} - safeRangeMin={maxCellMeasurement.range[0]!!} - safeRangeMax={maxCellMeasurement.range[1]!!} - units="V" - /> - <BarIndicator - icon={thunderIcon} - title="Min Cell" - getValue = {minCellMeasurement.getUpdate} - safeRangeMin={minCellMeasurement.range[0]!!} - safeRangeMax={minCellMeasurement.range[1]!!} - units="V" - /> - <BarIndicator - icon={thunderIcon} - title="Voltage" - getValue = {voltageMeasurement.getUpdate} - safeRangeMin={voltageMeasurement.range[0]!!} - safeRangeMax={voltageMeasurement.range[1]!!} - units="V" - /> - </IndicatorStack> - ) -}) + const stateOfChargeMeasurement = getNumericMeasurementInfo( + stateOfChargeMeasurementId + ); + const temperatureMeasurement = getNumericMeasurementInfo( + temperatureMeasurementId + ); + const maxCellMeasurement = + getNumericMeasurementInfo(maxCellMeasurementId); + const minCellMeasurement = + getNumericMeasurementInfo(minCellMeasurementId); + const voltageMeasurement = + getNumericMeasurementInfo(voltageMeasurementId); + const isBalancingMeasurement = getBooleanMeasurementInfo( + isBalancingMeasurementId + ); + + const [isBalancing, setIsBalancing] = useState(false); + useGlobalTicker(() => + setIsBalancing(isBalancingMeasurement.getUpdate()) + ); + + return ( + <IndicatorStack + className={isBalancing ? styles.balancing : styles.idle} + > + <BarIndicator + icon={batteryIcon} + title="SoC" + getValue={stateOfChargeMeasurement.getUpdate} + safeRangeMin={stateOfChargeMeasurement.range[0]!!} + safeRangeMax={stateOfChargeMeasurement.range[1]!!} + warningRangeMin={stateOfChargeMeasurement.warningRange[0]!!} + warningRangeMax={stateOfChargeMeasurement.warningRange[1]!!} + units={stateOfChargeMeasurement.units} + /> + <BarIndicator + icon={thermometerIcon} + title="Temperature" + getValue={temperatureMeasurement.getUpdate} + safeRangeMin={temperatureMeasurement.range[0]!!} + safeRangeMax={temperatureMeasurement.range[1]!!} + warningRangeMin={temperatureMeasurement.warningRange[0]!!} + warningRangeMax={temperatureMeasurement.warningRange[1]!!} + units={temperatureMeasurement.units} + /> + <BarIndicator + icon={thunderIcon} + title="Max Cell" + getValue={maxCellMeasurement.getUpdate} + safeRangeMin={maxCellMeasurement.range[0]!!} + safeRangeMax={maxCellMeasurement.range[1]!!} + warningRangeMin={maxCellMeasurement.warningRange[0]!!} + warningRangeMax={maxCellMeasurement.warningRange[1]!!} + units={maxCellMeasurement.units} + /> + <BarIndicator + icon={thunderIcon} + title="Min Cell" + getValue={minCellMeasurement.getUpdate} + safeRangeMin={minCellMeasurement.range[0]!!} + safeRangeMax={minCellMeasurement.range[1]!!} + warningRangeMin={minCellMeasurement.warningRange[0]!!} + warningRangeMax={minCellMeasurement.warningRange[1]!!} + units={minCellMeasurement.units} + /> + <BarIndicator + icon={thunderIcon} + title="Voltage" + getValue={voltageMeasurement.getUpdate} + safeRangeMin={voltageMeasurement.range[0]!!} + safeRangeMax={voltageMeasurement.range[1]!!} + warningRangeMin={voltageMeasurement.warningRange[0]!!} + warningRangeMax={voltageMeasurement.warningRange[1]!!} + units={voltageMeasurement.units} + /> + </IndicatorStack> + ); + } +); diff --git a/control-station/src/pages/VehiclePage/Boards/OBCCU/GeneralInfo/GeneralInfo.module.scss b/control-station/src/pages/VehiclePage/Boards/OBCCU/GeneralInfo/GeneralInfo.module.scss deleted file mode 100644 index 79e951895..000000000 --- a/control-station/src/pages/VehiclePage/Boards/OBCCU/GeneralInfo/GeneralInfo.module.scss +++ /dev/null @@ -1,5 +0,0 @@ -.generalInfo { - display: grid; - grid-template-columns: 1fr 1fr 1fr; - gap: 1rem; -} diff --git a/control-station/src/pages/VehiclePage/Boards/OBCCU/GeneralInfo/GeneralInfo.tsx b/control-station/src/pages/VehiclePage/Boards/OBCCU/GeneralInfo/GeneralInfo.tsx deleted file mode 100644 index 7675bce3a..000000000 --- a/control-station/src/pages/VehiclePage/Boards/OBCCU/GeneralInfo/GeneralInfo.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { - BooleanMeasurement, - EnumMeasurement, - GaugeTag, - NumericMeasurement, -} from "common"; -import styles from "./GeneralInfo.module.scss"; -import { BarTag } from "components/BarTag/BarTag"; -import { ValueDataTag } from "components/ValueDataTag/ValueDataTag"; - -type Props = { - maximumCell1: NumericMeasurement; - maximumCell2: NumericMeasurement; - maximumCell3: NumericMeasurement; - - minimumCell1: NumericMeasurement; - minimumCell2: NumericMeasurement; - minimumCell3: NumericMeasurement; - - totalVoltageHigh: NumericMeasurement; - drift: BooleanMeasurement; -}; - -export const GeneralInfo = (props: Props) => { - return ( - <div className={styles.generalInfo}> - <BarTag - barType="range" - measurement={props.maximumCell1} - /> - <BarTag - barType="range" - measurement={props.maximumCell2} - /> - <BarTag - barType="range" - measurement={props.maximumCell3} - /> - <BarTag - barType="range" - measurement={props.minimumCell1} - /> - <BarTag - barType="range" - measurement={props.minimumCell2} - /> - <BarTag - barType="range" - measurement={props.minimumCell3} - /> - <BarTag - barType="range" - measurement={props.totalVoltageHigh} - /> - <ValueDataTag measurement={props.drift} /> - </div> - ); -}; diff --git a/control-station/src/pages/VehiclePage/Boards/OBCCU/OBCCU.module.scss b/control-station/src/pages/VehiclePage/Boards/OBCCU/OBCCU.module.scss index d8f9cb982..4a9521e26 100644 --- a/control-station/src/pages/VehiclePage/Boards/OBCCU/OBCCU.module.scss +++ b/control-station/src/pages/VehiclePage/Boards/OBCCU/OBCCU.module.scss @@ -1,27 +1,67 @@ -@use "src/styles/fonts"; +@use 'src/styles/fonts'; -.obccu { - min-width: fit-content; - width: 100%; -} - -.batteryRow { +.hv_battery { display: flex; - justify-content: center; - gap: 1rem; + flex-flow: row; + justify-content: space-between; + gap: 0.5rem; + + .column { + display: flex; + flex-flow: column; + justify-content: space-between; + gap: 0.5rem; + } } -.centerPiece { - position: relative; +.middle_piece { display: flex; - align-items: center; - padding: .4rem; - gap: .5rem; - background-color: hsla(207, 89%, 96%, 1); -} + flex-flow: column; + justify-content: space-around; -.connectorIndicator { - position: absolute; - color: #3C7A8D; + min-width: 58px; + border-radius: 10px; + + padding: 0 0.35rem; + + color: #3c7a8d; font-size: map-get($map: fonts.$font-sizes, $key: x-small); -} \ No newline at end of file + + background-color: #edf6fe; + + position: relative; + + .xt90:nth-child(even) { + transform: rotate(180deg); + } + + .connector_row { + display: flex; + flex-flow: row; + justify-content: space-between; + } + + .cell_legend { + position: absolute; + + &.one { + top: 0rem; + left: 0.5rem; + } + + &.ten { + top: 0rem; + right: 0.5rem; + } + + &.five { + bottom: 0rem; + left: 0.5rem; + } + + &.six { + bottom: 0rem; + right: 0.5rem; + } + } +} diff --git a/control-station/src/pages/VehiclePage/Boards/OBCCU/OBCCUBatteries.tsx b/control-station/src/pages/VehiclePage/Boards/OBCCU/OBCCUBatteries.tsx index 45a3fc121..dfeeb1495 100644 --- a/control-station/src/pages/VehiclePage/Boards/OBCCU/OBCCUBatteries.tsx +++ b/control-station/src/pages/VehiclePage/Boards/OBCCU/OBCCUBatteries.tsx @@ -1,135 +1,183 @@ -import { Window } from "components/Window/Window"; -import styles from "./OBCCU.module.scss"; -import { ObccuMeasurements } from "common"; -import { BatteryPack } from "pages/VehiclePage/Boards/OBCCU/BatteryPack/BatteryPack"; -import { BatteryConnector } from "components/BatteryConnector/BatteryConnector"; +import { Window } from 'components/Window/Window'; +import styles from './OBCCU.module.scss'; +import { ObccuMeasurements } from 'common'; +import { BatteryPack } from 'pages/VehiclePage/Boards/OBCCU/BatteryPack/BatteryPack'; +import { ReactComponent as XT90 } from '../../../../assets/svg/XT90.svg'; export const OBCCUBatteries = () => { return ( <Window title="OBCCU"> - <div className={styles.obccu}> - <div className={styles.batteryRow}> + <div className={styles.hv_battery}> + <div className={styles.column}> <BatteryPack - stateOfChargeMeasurementId={ObccuMeasurements.SOC1} - temperatureMeasurementId={ObccuMeasurements.battery_temperature_1} + stateOfChargeMeasurementId={ + ObccuMeasurements.stateOfCharge1 + } + temperatureMeasurementId={ + ObccuMeasurements.batteryTemperature1 + } maxCellMeasurementId={ObccuMeasurements.maximumCell1} minCellMeasurementId={ObccuMeasurements.minimumCell1} voltageMeasurementId={ObccuMeasurements.totalVoltage1} + isBalancingMeasurementId={ + ObccuMeasurements.isBalancing1 + } /> - <div className={styles.centerPiece}> - <div style={{ - top: "5%", - left: "10%", - }} className={styles.connectorIndicator}> - 1 - </div> - <div style={{ - top: "5%", - right: "10%", - }} className={styles.connectorIndicator}> - 10 - </div> - <BatteryConnector rotate /> - <BatteryConnector /> - </div> - <BatteryPack - stateOfChargeMeasurementId={ObccuMeasurements.SOC10} - temperatureMeasurementId={ObccuMeasurements.battery_temperature_10} - maxCellMeasurementId={ObccuMeasurements.maximumCell10} - minCellMeasurementId={ObccuMeasurements.minimumCell10} - voltageMeasurementId={ObccuMeasurements.totalVoltage10} - /> - </div> - <div className={styles.batteryRow}> <BatteryPack - stateOfChargeMeasurementId={ObccuMeasurements.SOC2} - temperatureMeasurementId={ObccuMeasurements.battery_temperature_2} + stateOfChargeMeasurementId={ + ObccuMeasurements.stateOfCharge2 + } + temperatureMeasurementId={ + ObccuMeasurements.batteryTemperature2 + } maxCellMeasurementId={ObccuMeasurements.maximumCell2} minCellMeasurementId={ObccuMeasurements.minimumCell2} voltageMeasurementId={ObccuMeasurements.totalVoltage2} + isBalancingMeasurementId={ + ObccuMeasurements.isBalancing2 + } /> - <div className={styles.centerPiece}> - <BatteryConnector rotate /> - <BatteryConnector /> - </div> - <BatteryPack - stateOfChargeMeasurementId={ObccuMeasurements.SOC9} - temperatureMeasurementId={ObccuMeasurements.battery_temperature_9} - maxCellMeasurementId={ObccuMeasurements.maximumCell9} - minCellMeasurementId={ObccuMeasurements.minimumCell9} - voltageMeasurementId={ObccuMeasurements.totalVoltage9} - /> - </div> - <div className={styles.batteryRow}> <BatteryPack - stateOfChargeMeasurementId={ObccuMeasurements.SOC3} - temperatureMeasurementId={ObccuMeasurements.battery_temperature_3} + stateOfChargeMeasurementId={ + ObccuMeasurements.stateOfCharge3 + } + temperatureMeasurementId={ + ObccuMeasurements.batteryTemperature3 + } maxCellMeasurementId={ObccuMeasurements.maximumCell3} minCellMeasurementId={ObccuMeasurements.minimumCell3} voltageMeasurementId={ObccuMeasurements.totalVoltage3} + isBalancingMeasurementId={ + ObccuMeasurements.isBalancing3 + } /> - <div className={styles.centerPiece}> - <BatteryConnector rotate /> - <BatteryConnector /> - </div> - <BatteryPack - stateOfChargeMeasurementId={ObccuMeasurements.SOC8} - temperatureMeasurementId={ObccuMeasurements.battery_temperature_8} - maxCellMeasurementId={ObccuMeasurements.maximumCell8} - minCellMeasurementId={ObccuMeasurements.minimumCell8} - voltageMeasurementId={ObccuMeasurements.totalVoltage8} - /> - </div> - <div className={styles.batteryRow}> <BatteryPack - stateOfChargeMeasurementId={ObccuMeasurements.SOC4} - temperatureMeasurementId={ObccuMeasurements.battery_temperature_4} + stateOfChargeMeasurementId={ + ObccuMeasurements.stateOfCharge4 + } + temperatureMeasurementId={ + ObccuMeasurements.batteryTemperature4 + } maxCellMeasurementId={ObccuMeasurements.maximumCell4} minCellMeasurementId={ObccuMeasurements.minimumCell4} voltageMeasurementId={ObccuMeasurements.totalVoltage4} + isBalancingMeasurementId={ + ObccuMeasurements.isBalancing4 + } /> - <div className={styles.centerPiece}> - <BatteryConnector rotate /> - <BatteryConnector /> - </div> - <BatteryPack - stateOfChargeMeasurementId={ObccuMeasurements.SOC7} - temperatureMeasurementId={ObccuMeasurements.battery_temperature_7} - maxCellMeasurementId={ObccuMeasurements.maximumCell7} - minCellMeasurementId={ObccuMeasurements.minimumCell7} - voltageMeasurementId={ObccuMeasurements.totalVoltage7} - /> - </div> - <div className={styles.batteryRow}> <BatteryPack - stateOfChargeMeasurementId={ObccuMeasurements.SOC5} - temperatureMeasurementId={ObccuMeasurements.battery_temperature_5} + stateOfChargeMeasurementId={ + ObccuMeasurements.stateOfCharge5 + } + temperatureMeasurementId={ + ObccuMeasurements.batteryTemperature5 + } maxCellMeasurementId={ObccuMeasurements.maximumCell5} minCellMeasurementId={ObccuMeasurements.minimumCell5} voltageMeasurementId={ObccuMeasurements.totalVoltage5} + isBalancingMeasurementId={ + ObccuMeasurements.isBalancing5 + } /> - <div className={styles.centerPiece}> - <div style={{ - bottom: "5%", - left: "10%", - }} className={styles.connectorIndicator}> - 5 - </div> - <div style={{ - bottom: "5%", - right: "10%", - }} className={styles.connectorIndicator}> - 6 - </div> - <BatteryConnector rotate /> - <BatteryConnector /> + </div> + <div className={styles.middle_piece}> + <p className={`${styles.cell_legend} ${styles.one}`}>1</p> + <p className={`${styles.cell_legend} ${styles.ten}`}>10</p> + + <div className={styles.connector_row}> + <XT90 className={styles.xt90} /> + <XT90 className={styles.xt90} /> + </div> + <div className={styles.connector_row}> + <XT90 className={styles.xt90} /> + <XT90 className={styles.xt90} /> + </div> + <div className={styles.connector_row}> + <XT90 className={styles.xt90} /> + <XT90 className={styles.xt90} /> </div> + <div className={styles.connector_row}> + <XT90 className={styles.xt90} /> + <XT90 className={styles.xt90} /> + </div> + <div className={styles.connector_row}> + <XT90 className={styles.xt90} /> + <XT90 className={styles.xt90} /> + </div> + + <p className={`${styles.cell_legend} ${styles.five}`}>5</p> + <p className={`${styles.cell_legend} ${styles.six}`}>6</p> + </div> + <div className={styles.column}> + <BatteryPack + stateOfChargeMeasurementId={ + ObccuMeasurements.stateOfCharge10 + } + temperatureMeasurementId={ + ObccuMeasurements.batteryTemperature10 + } + maxCellMeasurementId={ObccuMeasurements.maximumCell10} + minCellMeasurementId={ObccuMeasurements.minimumCell10} + voltageMeasurementId={ObccuMeasurements.totalVoltage10} + isBalancingMeasurementId={ + ObccuMeasurements.isBalancing10 + } + /> + <BatteryPack + stateOfChargeMeasurementId={ + ObccuMeasurements.stateOfCharge9 + } + temperatureMeasurementId={ + ObccuMeasurements.batteryTemperature9 + } + maxCellMeasurementId={ObccuMeasurements.maximumCell9} + minCellMeasurementId={ObccuMeasurements.minimumCell9} + voltageMeasurementId={ObccuMeasurements.totalVoltage9} + isBalancingMeasurementId={ + ObccuMeasurements.isBalancing9 + } + /> + <BatteryPack + stateOfChargeMeasurementId={ + ObccuMeasurements.stateOfCharge8 + } + temperatureMeasurementId={ + ObccuMeasurements.batteryTemperature8 + } + maxCellMeasurementId={ObccuMeasurements.maximumCell8} + minCellMeasurementId={ObccuMeasurements.minimumCell8} + voltageMeasurementId={ObccuMeasurements.totalVoltage8} + isBalancingMeasurementId={ + ObccuMeasurements.isBalancing8 + } + /> + <BatteryPack + stateOfChargeMeasurementId={ + ObccuMeasurements.stateOfCharge7 + } + temperatureMeasurementId={ + ObccuMeasurements.batteryTemperature7 + } + maxCellMeasurementId={ObccuMeasurements.maximumCell7} + minCellMeasurementId={ObccuMeasurements.minimumCell7} + voltageMeasurementId={ObccuMeasurements.totalVoltage7} + isBalancingMeasurementId={ + ObccuMeasurements.isBalancing7 + } + /> <BatteryPack - stateOfChargeMeasurementId={ObccuMeasurements.SOC6} - temperatureMeasurementId={ObccuMeasurements.battery_temperature_6} + stateOfChargeMeasurementId={ + ObccuMeasurements.stateOfCharge6 + } + temperatureMeasurementId={ + ObccuMeasurements.batteryTemperature6 + } maxCellMeasurementId={ObccuMeasurements.maximumCell6} minCellMeasurementId={ObccuMeasurements.minimumCell6} voltageMeasurementId={ObccuMeasurements.totalVoltage6} + isBalancingMeasurementId={ + ObccuMeasurements.isBalancing6 + } /> </div> </div> diff --git a/control-station/src/pages/VehiclePage/Boards/OBCCU/OBCCUGeneralInfo.tsx b/control-station/src/pages/VehiclePage/Boards/OBCCU/OBCCUGeneralInfo.tsx index 42c9ce1a7..7c9201f75 100644 --- a/control-station/src/pages/VehiclePage/Boards/OBCCU/OBCCUGeneralInfo.tsx +++ b/control-station/src/pages/VehiclePage/Boards/OBCCU/OBCCUGeneralInfo.tsx @@ -1,119 +1,86 @@ -import { ObccuMeasurements, useMeasurementsStore } from "common" -import { IndicatorStack } from "components/IndicatorStack/IndicatorStack" -import { StateIndicator } from "components/StateIndicator/StateIndicator" -import { Window } from "components/Window/Window" -import batteryIcon from "assets/svg/battery-filled.svg" -import thunderIcon from "assets/svg/thunder-filled.svg" -import { GaugeTag } from "components/GaugeTag/GaugeTag" -import { BarIndicator } from "components/BarIndicator/BarIndicator" +import { ObccuMeasurements, useMeasurementsStore } from 'common'; +import { IndicatorStack } from 'components/IndicatorStack/IndicatorStack'; +import { StateIndicator } from 'components/StateIndicator/StateIndicator'; +import { Window } from 'components/Window/Window'; +import batteryIcon from 'assets/svg/battery-filled.svg'; +import thunderIcon from 'assets/svg/thunder-filled.svg'; +import { GaugeTag } from 'components/GaugeTag/GaugeTag'; +import pluggedIcon from 'assets/svg/plugged-icon.svg'; export const OBCCUGeneralInfo = () => { - - const getNumericMeasurementInfo = useMeasurementsStore(state => state.getNumericMeasurementInfo) - const totalVoltageHigh = getNumericMeasurementInfo(ObccuMeasurements.totalVoltageHigh) - // const inverterTemperature = getNumericMeasurementInfo(ObccuMeasurements.inverterTemperature) - // const transformerTemperature = getNumericMeasurementInfo(ObccuMeasurements.transformerTemperature) - // const resonantTankTemperature = getNumericMeasurementInfo(ObccuMeasurements.resonantTankTemperature) - // const rectifierTemperature = getNumericMeasurementInfo(ObccuMeasurements.rectifierTemperature) - + const getNumericMeasurementInfo = useMeasurementsStore( + (state) => state.getNumericMeasurementInfo + ); + + const totalVoltageHigh = getNumericMeasurementInfo( + ObccuMeasurements.totalVoltageHigh + ); + const dischargeCurrent = getNumericMeasurementInfo( + ObccuMeasurements.dischargeCurrent + ); + return ( <Window title="OBCCU"> - <div style={{ - display: "flex", - gap: "1rem", - height: "100%", - }} + <div + style={{ + display: 'flex', + gap: '1rem', + height: '100%', + }} > - <div style={{ - flex: "1", - display: "flex", - flexDirection: "column", - gap: ".5rem", - }}> + <div + style={{ + flex: '1', + display: 'flex', + flexDirection: 'column', + gap: '.5rem', + }} + > <IndicatorStack> <StateIndicator measurementId={ObccuMeasurements.generalState} - icon={thunderIcon} - /> - <StateIndicator - measurementId={ObccuMeasurements.generalState} - icon={batteryIcon} + icon={pluggedIcon} /> </IndicatorStack> - <div style={{ - display: "flex", - gap: "1rem", - }}> - <GaugeTag - name={totalVoltageHigh.name} - units={totalVoltageHigh.units} + <div + style={{ + display: 'flex', + gap: '1rem', + }} + > + <GaugeTag + id="obccu_general_voltage" + name={'Voltage'} + units={'Volts'} getUpdate={totalVoltageHigh.getUpdate} strokeWidth={120} - min={totalVoltageHigh.range[0] ?? 0} - max={totalVoltageHigh.range[1] ?? 100} + min={totalVoltageHigh.warningRange[0] ?? 225} + max={totalVoltageHigh.warningRange[1] ?? 252} /> - <GaugeTag - name={totalVoltageHigh.name} - units={totalVoltageHigh.units} - getUpdate={totalVoltageHigh.getUpdate} + <GaugeTag + id="obccu_general_current" + name={'Current'} + units={'Amps'} + getUpdate={dischargeCurrent.getUpdate} strokeWidth={120} - min={totalVoltageHigh.range[0] ?? 0} - max={totalVoltageHigh.range[1] ?? 100} + min={dischargeCurrent.warningRange[0] ?? 0} + max={dischargeCurrent.warningRange[1] ?? 100} /> </div> - </div> - <div style={{ - flex: "1", - display: "flex", - flexDirection: "column", - justifyContent: "space-around", - gap: ".5rem", - }}> <IndicatorStack> - <StateIndicator - measurementId={ObccuMeasurements.generalState} - icon={thunderIcon} - /> - </IndicatorStack> - - {/* <IndicatorStack> - <BarIndicator - icon={batteryIcon} - title="Inverter" - getValue={inverterTemperature.getUpdate} - safeRangeMin={inverterTemperature.range[0] ?? 0} - safeRangeMax={inverterTemperature.range[1] ?? 100} - units="ºC" - /> - <BarIndicator - icon={batteryIcon} - title="Transformer" - getValue={transformerTemperature.getUpdate} - safeRangeMin={transformerTemperature.range[0] ?? 0} - safeRangeMax={transformerTemperature.range[1] ?? 100} - units="ºC" - /> - <BarIndicator + <StateIndicator + measurementId={ObccuMeasurements.contactorsState} icon={batteryIcon} - title="Resonant Tank" - getValue={resonantTankTemperature.getUpdate} - safeRangeMin={resonantTankTemperature.range[0] ?? 0} - safeRangeMax={resonantTankTemperature.range[1] ?? 100} - units="ºC" /> - <BarIndicator - icon={batteryIcon} - title="Rectifier" - getValue={rectifierTemperature.getUpdate} - safeRangeMin={rectifierTemperature.range[0] ?? 0} - safeRangeMax={rectifierTemperature.range[1] ?? 100} - units="ºC" + <StateIndicator + measurementId={ObccuMeasurements.imdState} + icon={thunderIcon} /> - </IndicatorStack> */} + </IndicatorStack> </div> </div> </Window> - ) -} + ); +}; diff --git a/control-station/src/pages/VehiclePage/Boards/PCU/MotorInfo/MotorInfo.module.scss b/control-station/src/pages/VehiclePage/Boards/PCU/MotorInfo/MotorInfo.module.scss deleted file mode 100644 index 6fdb787db..000000000 --- a/control-station/src/pages/VehiclePage/Boards/PCU/MotorInfo/MotorInfo.module.scss +++ /dev/null @@ -1,15 +0,0 @@ -.motorInfoWrapper { - display: flex; - flex-direction: column; - gap: 1rem; -} - -.chart { - --bg-color: #eef6f7; -} - -.motorTemp { - width: 100%; - height: 100px; - background-color: rgb(126, 165, 48); -} diff --git a/control-station/src/pages/VehiclePage/Boards/PCU/MotorInfo/MotorInfo.tsx b/control-station/src/pages/VehiclePage/Boards/PCU/MotorInfo/MotorInfo.tsx deleted file mode 100644 index 769653bcf..000000000 --- a/control-station/src/pages/VehiclePage/Boards/PCU/MotorInfo/MotorInfo.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import styles from "./MotorInfo.module.scss"; -import { - LineDescription, - Measurement, - MeasurementId, - NumericMeasurement, - isNumericMeasurement, - useMeasurementsStore, -} from "common"; -import { ColorfulChart } from "common"; - -type Props = { - title: string; - motorCurrentU: NumericMeasurement; - motorCurrentV: NumericMeasurement; - motorCurrentW: NumericMeasurement; -}; - -export const MotorInfo = ({ - title, - motorCurrentU, - motorCurrentV, - motorCurrentW, -}: Props) => { - const getMeasurement = useMeasurementsStore((state) => state.getMeasurement); - - return ( - <div className={styles.motorInfoWrapper}> - <ColorfulChart - className={styles.chart} - title={title} - length={100} - items={[ - getItemFromMeasurement(motorCurrentU, getMeasurement), - getItemFromMeasurement(motorCurrentV, getMeasurement), - getItemFromMeasurement(motorCurrentW, getMeasurement), - ]} - /> - </div> - ); -}; - -function getItemFromMeasurement(meas: NumericMeasurement, getMeasurement: (id: MeasurementId) => Measurement): LineDescription { - return { - id: meas.id, - name: meas.name, - color: "red", - getUpdate: () => getMeasurementValue(meas.id, getMeasurement), - range: meas.safeRange, - }; -} - -function getMeasurementValue(id: string, getMeasurement: (id: MeasurementId) => Measurement): number { - const measurement = getMeasurement(id) - - if (!measurement) { - return 0; - } - - if (isNumericMeasurement(measurement)) { - return measurement.value.last; - } else { - return 0; - } -} diff --git a/control-station/src/pages/VehiclePage/Boards/PCU/PCU.module.scss b/control-station/src/pages/VehiclePage/Boards/PCU/PCU.module.scss index 1e9b582b2..0dac3bbc7 100644 --- a/control-station/src/pages/VehiclePage/Boards/PCU/PCU.module.scss +++ b/control-station/src/pages/VehiclePage/Boards/PCU/PCU.module.scss @@ -1,29 +1,43 @@ -.pcuSectionWrapper { +.container { + display: flex; + flex-flow: column; + max-width: 670px; +} +.content { + display: flex; + flex-flow: column; + justify-content: center; + align-items: center; + gap: 1rem; + width: 100%; + margin: 1rem 0; +} + +.current_display { + display: flex; + flex-flow: row; + align-items: center; + justify-content: center; + width: 100%; +} + +.current_chart { + display: flex; + flex-flow: column; + align-items: center; + justify-content: center; + gap: 5px; width: 100%; - display: grid; - grid-template: - "velGauge accGauge" auto - "motorCurrent1 motorCurrent2" auto - "motorTemp boardTemp" auto / auto auto; - gap: 1.5rem; - overflow-y: auto; +} + +.chart_title { + margin: 0; +} + +.chart { + width: 100%; +} - > :nth-child(1) { - grid-area: velGauge; - } - > :nth-child(2) { - grid-area: accGauge; - } - > :nth-child(3) { - grid-area: motorCurrent1; - } - > :nth-child(4) { - grid-area: motorCurrent2; - } - > :nth-child(5) { - grid-area: motorTemp; - } - > :nth-child(6) { - grid-area: boardTemp; - } +.frequency { + max-width: 15rem; } diff --git a/control-station/src/pages/VehiclePage/Boards/PCU/PCU.tsx b/control-station/src/pages/VehiclePage/Boards/PCU/PCU.tsx index e12669445..90b30d9fb 100644 --- a/control-station/src/pages/VehiclePage/Boards/PCU/PCU.tsx +++ b/control-station/src/pages/VehiclePage/Boards/PCU/PCU.tsx @@ -1,64 +1,268 @@ -import styles from "./PCU.module.scss"; -import { Window } from "components/Window/Window"; -import { VectorGauge } from "./VectorGauge/VectorGauge"; -import { MotorInfo } from "./MotorInfo/MotorInfo"; -import { PcuMeasurements } from "common"; -import { TempTag } from "./TempTag/TempTag"; -import motorUrl from "assets/images/motor.png"; -import pcbUrl from "assets/images/pcb.png"; -export const PCU = (props: PcuMeasurements) => { +import styles from './PCU.module.scss'; +import { Window } from 'components/Window/Window'; +import { ColorfulChart, PcuMeasurements, useMeasurementsStore } from 'common'; +import DLIM from 'assets/svg/dlim.svg'; +import { IndicatorStack } from 'components/IndicatorStack/IndicatorStack'; +import { BarIndicator } from 'components/BarIndicator/BarIndicator'; +import thermometerIcon from 'assets/svg/thermometer-filled.svg'; +import { StateIndicator } from 'components/StateIndicator/StateIndicator'; +import thunderIcon from 'assets/svg/thunder-filled.svg'; +import pluggedIcon from 'assets/svg/plugged-icon.svg'; + +export const PCU = () => { + const getNumericMeasurementInfo = useMeasurementsStore( + (state) => state.getNumericMeasurementInfo + ); + + const motorAPeakCurrent = getNumericMeasurementInfo( + PcuMeasurements.motorAPeakCurrent + ); + const motorACurrentU = getNumericMeasurementInfo( + PcuMeasurements.motorACurrentU + ); + const motorACurrentV = getNumericMeasurementInfo( + PcuMeasurements.motorACurrentV + ); + const motorACurrentW = getNumericMeasurementInfo( + PcuMeasurements.motorACurrentW + ); + const motorATemp = getNumericMeasurementInfo(PcuMeasurements.motorATemp); + + const motorBPeakCurrent = getNumericMeasurementInfo( + PcuMeasurements.motorBPeakCurrent + ); + const motorBCurrentU = getNumericMeasurementInfo( + PcuMeasurements.motorBCurrentU + ); + const motorBCurrentV = getNumericMeasurementInfo( + PcuMeasurements.motorBCurrentV + ); + const motorBCurrentW = getNumericMeasurementInfo( + PcuMeasurements.motorBCurrentW + ); + const motorBTemp = getNumericMeasurementInfo(PcuMeasurements.motorBTemp); + + const frequency = getNumericMeasurementInfo(PcuMeasurements.frequency); + return ( <Window title="PCU"> - <section className={styles.pcuSectionWrapper}> - <VectorGauge - x={props.velocity} - y={props.velocity} - z={props.velocity} - /> - <VectorGauge - x={props.accel_x} - y={props.accel_y} - z={props.accel_z} - /> - <MotorInfo - title="Motor 1" - motorCurrentU={props.motor_a_current_u} - motorCurrentV={props.motor_a_current_v} - motorCurrentW={props.motor_a_current_w} - /> - <MotorInfo - title="Motor 2" - motorCurrentU={props.motor_b_current_u} - motorCurrentV={props.motor_b_current_v} - motorCurrentW={props.motor_b_current_w} - /> - <TempTag - meas={props.max_motor_a_temperature} - icon={ - <img - src={motorUrl} - style={{ - objectFit: "contain", - width: "9rem", - height: "4rem", - }} + <div className={styles.container}> + <img src={DLIM} alt="DLIM" /> + + <div className={styles.content}> + <div className={styles.current_display}> + <div className={styles.current_chart}> + <p className={styles.chart_title}>Motor A</p> + <ColorfulChart + className={styles.chart} + length={35} + items={[ + motorACurrentU, + motorACurrentV, + motorACurrentW, + ]} + /> + <IndicatorStack> + <BarIndicator + title="Peak current" + icon={thunderIcon} + getValue={motorAPeakCurrent.getUpdate} + safeRangeMin={motorAPeakCurrent.range[0]!!} + safeRangeMax={motorAPeakCurrent.range[1]!!} + warningRangeMin={ + motorAPeakCurrent.warningRange[0]!! + } + warningRangeMax={ + motorAPeakCurrent.warningRange[1]!! + } + units={motorAPeakCurrent.units} + /> + <BarIndicator + title="Current U" + icon={thunderIcon} + getValue={motorACurrentU.getUpdate} + safeRangeMin={motorACurrentU.range[0]!!} + safeRangeMax={motorACurrentU.range[1]!!} + warningRangeMin={ + motorACurrentU.warningRange[0]!! + } + warningRangeMax={ + motorACurrentU.warningRange[1]!! + } + units={motorACurrentU.units} + color="#EE8735" + backgroundColor="#FFE7CF" + /> + <BarIndicator + title="Current V" + icon={thunderIcon} + getValue={motorACurrentV.getUpdate} + safeRangeMin={motorACurrentV.range[0]!!} + safeRangeMax={motorACurrentV.range[1]!!} + warningRangeMin={ + motorACurrentV.warningRange[0]!! + } + warningRangeMax={ + motorACurrentV.warningRange[1]!! + } + units={motorACurrentV.units} + color="#51C6EB" + backgroundColor="#CEF3FF" + /> + <BarIndicator + title="Current W" + icon={thunderIcon} + getValue={motorACurrentW.getUpdate} + safeRangeMin={motorACurrentW.range[0]!!} + safeRangeMax={motorACurrentW.range[1]!!} + warningRangeMin={ + motorACurrentW.warningRange[0]!! + } + warningRangeMax={ + motorACurrentW.warningRange[1]!! + } + units={motorACurrentW.units} + color="#7BEE35" + backgroundColor="#E5FFD4" + /> + </IndicatorStack> + + <IndicatorStack> + <BarIndicator + title="Temperature" + icon={thermometerIcon} + getValue={motorATemp.getUpdate} + safeRangeMin={motorATemp.range[0]!!} + safeRangeMax={motorATemp.range[1]!!} + warningRangeMin={ + motorATemp.warningRange[0]!! + } + warningRangeMax={ + motorATemp.warningRange[1]!! + } + units={motorATemp.units} + /> + </IndicatorStack> + </div> + + <div className={styles.current_chart}> + <p className={styles.chart_title}>Motor B</p> + <ColorfulChart + className={styles.chart} + length={35} + items={[ + motorBCurrentU, + motorBCurrentV, + motorBCurrentW, + ]} + /> + <IndicatorStack> + <BarIndicator + title="Peak current" + icon={thunderIcon} + getValue={motorBPeakCurrent.getUpdate} + safeRangeMin={motorBPeakCurrent.range[0]!!} + safeRangeMax={motorBPeakCurrent.range[1]!!} + warningRangeMin={ + motorBPeakCurrent.warningRange[0]!! + } + warningRangeMax={ + motorBPeakCurrent.warningRange[1]!! + } + units="A" + /> + <BarIndicator + title="Current U" + icon={thunderIcon} + getValue={motorBCurrentU.getUpdate} + safeRangeMin={motorBCurrentU.range[0]!!} + safeRangeMax={motorBCurrentU.range[1]!!} + warningRangeMin={ + motorBCurrentU.warningRange[0]!! + } + warningRangeMax={ + motorBCurrentU.warningRange[1]!! + } + units="A" + color="#EE8735" + backgroundColor="#FFE7CF" + /> + <BarIndicator + title="Current V" + icon={thunderIcon} + getValue={motorBCurrentV.getUpdate} + safeRangeMin={motorBCurrentV.range[0]!!} + safeRangeMax={motorBCurrentV.range[1]!!} + warningRangeMin={ + motorBCurrentV.warningRange[0]!! + } + warningRangeMax={ + motorBCurrentV.warningRange[1]!! + } + units="A" + color="#51C6EB" + backgroundColor="#CEF3FF" + /> + <BarIndicator + title="Current W" + icon={thunderIcon} + getValue={motorBCurrentW.getUpdate} + safeRangeMin={motorBCurrentW.range[0]!!} + safeRangeMax={motorBCurrentW.range[1]!!} + warningRangeMin={ + motorBCurrentW.warningRange[0]!! + } + warningRangeMax={ + motorBCurrentW.warningRange[1]!! + } + units="A" + color="#7BEE35" + backgroundColor="#E5FFD4" + /> + </IndicatorStack> + + <IndicatorStack> + <BarIndicator + title="Temperature" + icon={thermometerIcon} + getValue={motorBTemp.getUpdate} + safeRangeMin={motorBTemp.range[0]!!} + safeRangeMax={motorBTemp.range[1]!!} + warningRangeMin={ + motorBTemp.warningRange[0]!! + } + warningRangeMax={ + motorBTemp.warningRange[1]!! + } + units={motorBTemp.units} + /> + </IndicatorStack> + </div> + </div> + <IndicatorStack className={styles.frequency}> + <StateIndicator + measurementId={PcuMeasurements.generalState} + icon={pluggedIcon} /> - } - /> - <TempTag - meas={props.max_ppu_a_temperature} - icon={ - <img - src={pcbUrl} - style={{ - objectFit: "contain", - width: "4rem", - height: "4rem", - }} + <BarIndicator + title="Frequency" + icon={thunderIcon} + getValue={frequency.getUpdate} + safeRangeMin={frequency.range[0]!!} + safeRangeMax={frequency.range[1]!!} + warningRangeMin={frequency.warningRange[0]!!} + warningRangeMax={frequency.warningRange[1]!!} + units={frequency.units} /> - } + </IndicatorStack> + </div> + + <img + src={DLIM} + alt="DLIM" + style={{ transform: 'rotate(180deg' }} /> - </section> + </div> </Window> ); }; diff --git a/control-station/src/pages/VehiclePage/Boards/PCU/PPUInfo/PPUInfo.module.scss b/control-station/src/pages/VehiclePage/Boards/PCU/PPUInfo/PPUInfo.module.scss deleted file mode 100644 index 0e7104d9d..000000000 --- a/control-station/src/pages/VehiclePage/Boards/PCU/PPUInfo/PPUInfo.module.scss +++ /dev/null @@ -1,14 +0,0 @@ -.ppuInfoWrapper { - display: grid; - grid-template-columns: 1fr 1fr; - column-gap: 2rem; - row-gap: 1rem; // width: 100%; - // height: fit-content; - // display: flex; - // flex-direction: row; - // flex-wrap: wrap; - // row-gap: 1rem; - .tag { - width: 50%; - } -} diff --git a/control-station/src/pages/VehiclePage/Boards/PCU/PPUInfo/PPUInfo.tsx b/control-station/src/pages/VehiclePage/Boards/PCU/PPUInfo/PPUInfo.tsx deleted file mode 100644 index 2b7776f49..000000000 --- a/control-station/src/pages/VehiclePage/Boards/PCU/PPUInfo/PPUInfo.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { NumericMeasurement } from "common"; -import styles from "./PPUInfo.module.scss"; -import { BarTag } from "components/BarTag/BarTag"; - -type Props = { - batteryVoltage: NumericMeasurement; - batteryCurrent: NumericMeasurement; - temperature1: NumericMeasurement; - temperature2: NumericMeasurement; - temperature3: NumericMeasurement; -}; - -export const PPUInfo = ({ - batteryCurrent, - batteryVoltage, - temperature1, - temperature2, - temperature3, -}: Props) => { - return ( - <div className={styles.ppuInfoWrapper}> - <BarTag - barType="range" - measurement={batteryVoltage} - /> - <BarTag - barType="range" - measurement={batteryCurrent} - /> - <BarTag - barType="temp" - measurement={temperature1} - /> - <BarTag - barType="temp" - measurement={temperature2} - /> - <BarTag - barType="temp" - measurement={temperature3} - /> - </div> - ); -}; diff --git a/control-station/src/pages/VehiclePage/Boards/PCU/TempTag/TempTag.module.scss b/control-station/src/pages/VehiclePage/Boards/PCU/TempTag/TempTag.module.scss deleted file mode 100644 index d8f052cd3..000000000 --- a/control-station/src/pages/VehiclePage/Boards/PCU/TempTag/TempTag.module.scss +++ /dev/null @@ -1,33 +0,0 @@ -.tempTag { - display: grid; - grid-template: - "title title" auto - "icon value" auto/ auto auto; - - align-items: center; - - > :nth-child(1) { - grid-area: title; - } - > :nth-child(2) { - grid-area: icon; - } - > :nth-child(3) { - grid-area: value; - justify-self: end; - } - - background-color: var(--primary-95); - border-radius: 1rem; - padding: 1rem; - gap: 0.8rem; -} - -.title { - font-weight: 300; -} - -.value { - font-size: 1.6rem; - font-weight: 700; -} diff --git a/control-station/src/pages/VehiclePage/Boards/PCU/TempTag/TempTag.tsx b/control-station/src/pages/VehiclePage/Boards/PCU/TempTag/TempTag.tsx deleted file mode 100644 index 24c44a344..000000000 --- a/control-station/src/pages/VehiclePage/Boards/PCU/TempTag/TempTag.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { ReactNode } from "react"; -import styles from "./TempTag.module.scss"; -import { NumericMeasurement } from "common"; -import { StateOverlay } from "components/StateOverlay/StateOverlay"; -import { getState } from "state"; - -type Props = { - meas: NumericMeasurement; - icon: ReactNode; -}; - -export const TempTag = ({ meas, icon }: Props) => { - return ( - <StateOverlay state={getState(meas)}> - <div className={styles.tempTag}> - <div className={styles.title}>{meas.name}</div> - <div className={styles.icon}>{icon}</div> - <div className={styles.value}> - {`${meas.value.last.toFixed(2)} ${meas.units}`} - </div> - </div> - </StateOverlay> - ); -}; diff --git a/control-station/src/pages/VehiclePage/Boards/PCU/VectorGauge/DirectionTag/DirectionTag.module.scss b/control-station/src/pages/VehiclePage/Boards/PCU/VectorGauge/DirectionTag/DirectionTag.module.scss deleted file mode 100644 index eb6c9202d..000000000 --- a/control-station/src/pages/VehiclePage/Boards/PCU/VectorGauge/DirectionTag/DirectionTag.module.scss +++ /dev/null @@ -1,22 +0,0 @@ -@use "src/styles/fonts"; - -.directionTagWrapper { - display: flex; - flex-direction: column; - align-items: center; - gap: 0.3rem; - padding: 0.3rem 0.5rem; - background-color: hsl(0, 0%, 96%); - border-radius: 0.5rem; -} - -.valueWrapper { - display: flex; - gap: 0.5rem; - font-family: var(--font-mono); -} - -.axis { - font-weight: fonts.getFontWeight("bold"); - font-style: italic; -} diff --git a/control-station/src/pages/VehiclePage/Boards/PCU/VectorGauge/DirectionTag/DirectionTag.tsx b/control-station/src/pages/VehiclePage/Boards/PCU/VectorGauge/DirectionTag/DirectionTag.tsx deleted file mode 100644 index 8c5f21bb5..000000000 --- a/control-station/src/pages/VehiclePage/Boards/PCU/VectorGauge/DirectionTag/DirectionTag.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import styles from "./DirectionTag.module.scss"; - -type Props = { - axis: string; - value: number; - units: string; -}; -export const DirectionTag = ({ axis, value, units }: Props) => { - return ( - <div className={styles.directionTagWrapper}> - <span className={styles.axis}>{axis}</span> - <div className={styles.valueWrapper}> - <span className={styles.value}>{value.toFixed(2)}</span> - <span className={styles.units}>{units}</span> - </div> - </div> - ); -}; diff --git a/control-station/src/pages/VehiclePage/Boards/PCU/VectorGauge/VectorGauge.module.scss b/control-station/src/pages/VehiclePage/Boards/PCU/VectorGauge/VectorGauge.module.scss deleted file mode 100644 index 7060a9e3f..000000000 --- a/control-station/src/pages/VehiclePage/Boards/PCU/VectorGauge/VectorGauge.module.scss +++ /dev/null @@ -1,13 +0,0 @@ -.vectorGauge { - display: flex; - flex-direction: column; - align-items: center; - gap: 1rem; - font-size: 1.2rem; -} - -.directions { - display: flex; - gap: 1rem; - font-size: 1rem; -} diff --git a/control-station/src/pages/VehiclePage/Boards/PCU/VectorGauge/VectorGauge.tsx b/control-station/src/pages/VehiclePage/Boards/PCU/VectorGauge/VectorGauge.tsx deleted file mode 100644 index c8414be06..000000000 --- a/control-station/src/pages/VehiclePage/Boards/PCU/VectorGauge/VectorGauge.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import styles from "./VectorGauge.module.scss"; -import { GaugeTag } from "components/GaugeTag/GaugeTag"; -import { DirectionTag } from "./DirectionTag/DirectionTag"; -import { NumericMeasurement } from "common"; - -type Props = { - x: NumericMeasurement; - y: NumericMeasurement; - z: NumericMeasurement; -}; - -export const VectorGauge = ({ x, y, z }: Props) => { - return ( - <article className={styles.vectorGauge}> - <GaugeTag - measurement={x} - min={0} - max={100} - strokeWidth={145} - className={styles.velGauge} - /> - <div className={styles.directions}> - <DirectionTag - axis="x" - value={x.value.average} - units={"m"} - /> - <DirectionTag - axis="y" - value={y.value.average} - units={"m"} - /> - <DirectionTag - axis="z" - value={z.value.average} - units={"m"} - /> - </div> - </article> - ); -}; diff --git a/control-station/src/pages/VehiclePage/Boards/VCU/TrackVisualizer/TrackVisualizer.module.scss b/control-station/src/pages/VehiclePage/Boards/VCU/TrackVisualizer/TrackVisualizer.module.scss new file mode 100644 index 000000000..076a6dc0e --- /dev/null +++ b/control-station/src/pages/VehiclePage/Boards/VCU/TrackVisualizer/TrackVisualizer.module.scss @@ -0,0 +1,31 @@ +.container { + display: flex; + align-items: center; + justify-content: center; + flex-flow: column; + position: relative; + overflow: hidden; +} + +.track_container { + display: flex; + justify-content: center; + max-width: 167px; + + > img { + width: 100%; + height: 100%; + } +} + +.vehicle_container { + display: flex; + justify-content: center; + position: absolute; + + > img { + width: 100%; + height: 100%; + max-width: 69px; + } +} diff --git a/control-station/src/pages/VehiclePage/Boards/VCU/TrackVisualizer/TrackVisualizer.tsx b/control-station/src/pages/VehiclePage/Boards/VCU/TrackVisualizer/TrackVisualizer.tsx new file mode 100644 index 000000000..3a5879480 --- /dev/null +++ b/control-station/src/pages/VehiclePage/Boards/VCU/TrackVisualizer/TrackVisualizer.tsx @@ -0,0 +1,43 @@ +import styles from './TrackVisualizer.module.scss'; +import vehicleTrack from 'assets/svg/vehicle-track.svg'; +import vehicle from 'assets/svg/vesper-icon.svg'; +import { useGlobalTicker } from 'common'; +import { memo, useEffect, useRef, useState } from 'react'; +import { getPercentageFromRange } from 'state'; + +type Props = { + getUpdate: () => number; + rangeMin: number; + rangeMax: number; +}; + +export const TrackVisualizer = memo((props: Props) => { + const [valueState, setValueState] = useState<number>(0); + const percentage = useRef<number>(100); + + useGlobalTicker(() => { + setValueState(props.getUpdate()); + }); + + useEffect(() => { + percentage.current = + 100 - + getPercentageFromRange(valueState, props.rangeMin, props.rangeMax); + }); + + return ( + <div className={styles.container}> + <div className={styles.track_container}> + <img src={vehicleTrack} alt="Vehicle Track" /> + </div> + <div + className={styles.vehicle_container} + style={{ + top: percentage.current + '%', + }} + > + <img src={vehicle} alt="Vehicle" /> + </div> + </div> + ); +}); diff --git a/control-station/src/pages/VehiclePage/Boards/VCU/VCU.module.scss b/control-station/src/pages/VehiclePage/Boards/VCU/VCU.module.scss index c58e10b9a..2a583fb80 100644 --- a/control-station/src/pages/VehiclePage/Boards/VCU/VCU.module.scss +++ b/control-station/src/pages/VehiclePage/Boards/VCU/VCU.module.scss @@ -7,13 +7,13 @@ .row { display: flex; justify-content: center; - gap: .5rem; + gap: 0.5rem; } .trackContainer { display: flex; justify-content: center; - max-height: 25rem; + max-height: 629px; > img { width: 100%; @@ -38,4 +38,8 @@ flex-direction: column; height: fit-content; gap: 1rem; -} \ No newline at end of file +} + +.connections { + width: 100%; +} diff --git a/control-station/src/pages/VehiclePage/Boards/VCU/VCUBrakesInfo.tsx b/control-station/src/pages/VehiclePage/Boards/VCU/VCUBrakesInfo.tsx index f237b3b5c..107421756 100644 --- a/control-station/src/pages/VehiclePage/Boards/VCU/VCUBrakesInfo.tsx +++ b/control-station/src/pages/VehiclePage/Boards/VCU/VCUBrakesInfo.tsx @@ -1,39 +1,63 @@ -import styles from "./VCU.module.scss"; -import { Window } from "components/Window/Window"; -import { useMeasurementsStore, VcuMeasurements } from "common"; -import { IndicatorStack } from "components/IndicatorStack/IndicatorStack"; -import { BarIndicator } from "components/BarIndicator/BarIndicator"; -import thermometerIcon from "assets/svg/thermometer-filled.svg"; -import { BrakeVisualizer } from "components/BrakeVisualizer/BrakeVisualizer"; +import styles from './VCU.module.scss'; +import { Window } from 'components/Window/Window'; +import { useMeasurementsStore, VcuMeasurements } from 'common'; +import { IndicatorStack } from 'components/IndicatorStack/IndicatorStack'; +import { BarIndicator } from 'components/BarIndicator/BarIndicator'; +import pressureIcon from 'assets/svg/pressure-filled.svg'; +import { BrakeVisualizer } from 'components/BrakeVisualizer/BrakeVisualizer'; +import { StateIndicator } from 'components/StateIndicator/StateIndicator'; export const VCUBrakesInfo = () => { - - const getNumericMeasurementInfo = useMeasurementsStore(state => state.getNumericMeasurementInfo); - const getBooleanMeasurementInfo = useMeasurementsStore(state => state.getBooleanMeasurementInfo); - const reed1 = getBooleanMeasurementInfo(VcuMeasurements.reed1); - const reed2 = getBooleanMeasurementInfo(VcuMeasurements.reed2); - const reed3 = getBooleanMeasurementInfo(VcuMeasurements.reed2); - const reed4 = getBooleanMeasurementInfo(VcuMeasurements.reed2); - const bottleTemp1 = getNumericMeasurementInfo(VcuMeasurements.bottleTemp1); - const bottleTemp2 = getNumericMeasurementInfo(VcuMeasurements.bottleTemp2); - const highPressure = getNumericMeasurementInfo(VcuMeasurements.highPressure); + const getNumericMeasurementInfo = useMeasurementsStore( + (state) => state.getNumericMeasurementInfo + ); + const getEnumMeasurementInfo = useMeasurementsStore( + (state) => state.getEnumMeasurementInfo + ); + const reed1 = getEnumMeasurementInfo(VcuMeasurements.reed1); + const reed2 = getEnumMeasurementInfo(VcuMeasurements.reed2); + const reed3 = getEnumMeasurementInfo(VcuMeasurements.reed3); + const reed4 = getEnumMeasurementInfo(VcuMeasurements.reed4); + const highPressure = getNumericMeasurementInfo( + VcuMeasurements.highPressure + ); + const lowPressure1 = getNumericMeasurementInfo( + VcuMeasurements.lowPressure1 + ); + const lowPressure2 = getNumericMeasurementInfo( + VcuMeasurements.lowPressure2 + ); + const referencePressure = getNumericMeasurementInfo( + VcuMeasurements.referencePressure + ); return ( <Window title="VCU"> <div className={styles.vcuBrakesInfo}> - <div className={styles.brakesContainer}> <div className={styles.brakesColumn}> - <BrakeVisualizer getStatus={reed1.getUpdate} rotation="left" /> - <BrakeVisualizer getStatus={reed2.getUpdate} rotation="left" /> + <BrakeVisualizer + getStatus={reed1.getUpdate} + rotation="left" + /> + <BrakeVisualizer + getStatus={reed2.getUpdate} + rotation="left" + /> </div> <div className={styles.brakesColumn}> - <BrakeVisualizer getStatus={reed3.getUpdate} rotation="right" /> - <BrakeVisualizer getStatus={reed4.getUpdate} rotation="right" /> + <BrakeVisualizer + getStatus={reed3.getUpdate} + rotation="right" + /> + <BrakeVisualizer + getStatus={reed4.getUpdate} + rotation="right" + /> </div> </div> - <IndicatorStack> + {/* <IndicatorStack> <BarIndicator title="Bottle Temp" icon={thermometerIcon} @@ -50,39 +74,51 @@ export const VCUBrakesInfo = () => { safeRangeMax={bottleTemp2.range[1]!!} units="ºC" /> - </IndicatorStack> + </IndicatorStack> */} <IndicatorStack> <BarIndicator title="High Pressure" - icon={thermometerIcon} + icon={pressureIcon} getValue={highPressure.getUpdate} safeRangeMin={highPressure.range[0]!!} safeRangeMax={highPressure.range[1]!!} + warningRangeMin={highPressure.warningRange[0]!!} + warningRangeMax={highPressure.warningRange[1]!!} units="bar" /> + <StateIndicator + measurementId={VcuMeasurements.valveState} + icon={pressureIcon} + /> <BarIndicator - title="High Pressure" - icon={thermometerIcon} - getValue={highPressure.getUpdate} - safeRangeMin={highPressure.range[0]!!} - safeRangeMax={highPressure.range[1]!!} + title="Reference Pressure" + icon={pressureIcon} + getValue={referencePressure.getUpdate} + safeRangeMin={referencePressure.range[0]!!} + safeRangeMax={referencePressure.range[1]!!} + warningRangeMin={referencePressure.warningRange[0]!!} + warningRangeMax={referencePressure.warningRange[1]!!} units="bar" /> <BarIndicator - title="High Pressure" - icon={thermometerIcon} - getValue={highPressure.getUpdate} - safeRangeMin={highPressure.range[0]!!} - safeRangeMax={highPressure.range[1]!!} + title="Low Pressure 1" + icon={pressureIcon} + getValue={lowPressure1.getUpdate} + safeRangeMin={lowPressure1.range[0]!!} + safeRangeMax={lowPressure1.range[1]!!} + warningRangeMin={lowPressure1.warningRange[0]!!} + warningRangeMax={lowPressure1.warningRange[1]!!} units="bar" /> <BarIndicator - title="High Pressure" - icon={thermometerIcon} - getValue={highPressure.getUpdate} - safeRangeMin={highPressure.range[0]!!} - safeRangeMax={highPressure.range[1]!!} + title="Low Pressure 2" + icon={pressureIcon} + getValue={lowPressure2.getUpdate} + safeRangeMin={lowPressure2.range[0]!!} + safeRangeMax={lowPressure2.range[1]!!} + warningRangeMin={lowPressure2.warningRange[0]!!} + warningRangeMax={lowPressure2.warningRange[1]!!} units="bar" /> </IndicatorStack> diff --git a/control-station/src/pages/VehiclePage/Boards/VCU/VCUConnectionsInfo.tsx b/control-station/src/pages/VehiclePage/Boards/VCU/VCUConnectionsInfo.tsx new file mode 100644 index 000000000..ea9180e0f --- /dev/null +++ b/control-station/src/pages/VehiclePage/Boards/VCU/VCUConnectionsInfo.tsx @@ -0,0 +1,42 @@ +import styles from './VCU.module.scss'; +import { Window } from 'components/Window/Window'; +import { IndicatorStack } from 'components/IndicatorStack/IndicatorStack'; +import pluggedIcon from 'assets/svg/plugged-icon.svg'; +import { StateIndicator } from 'components/StateIndicator/StateIndicator'; +import { VcuMeasurements } from 'common'; + +export const VCUConnectionsInfo = () => { + return ( + <Window title="VCU"> + <div + style={{ + display: 'flex', + flexFlow: 'column', + gap: '0.5rem', + width: '100%', + }} + > + <IndicatorStack> + <StateIndicator + measurementId={VcuMeasurements.generalState} + icon={pluggedIcon} + /> + </IndicatorStack> + <IndicatorStack className={styles.connections}> + <StateIndicator + measurementId={VcuMeasurements.pcuConnection} + icon={pluggedIcon} + /> + <StateIndicator + measurementId={VcuMeasurements.obccuConnection} + icon={pluggedIcon} + /> + <StateIndicator + measurementId={VcuMeasurements.lcuConnection} + icon={pluggedIcon} + /> + </IndicatorStack> + </div> + </Window> + ); +}; diff --git a/control-station/src/pages/VehiclePage/Boards/VCU/VCUPositionInfo.tsx b/control-station/src/pages/VehiclePage/Boards/VCU/VCUPositionInfo.tsx index d664976aa..6b9efc685 100644 --- a/control-station/src/pages/VehiclePage/Boards/VCU/VCUPositionInfo.tsx +++ b/control-station/src/pages/VehiclePage/Boards/VCU/VCUPositionInfo.tsx @@ -1,40 +1,63 @@ -import styles from "./VCU.module.scss"; -import { Window } from "components/Window/Window"; -import { useMeasurementsStore, VcuMeasurements } from "common"; -import { GaugeTag } from "components/GaugeTag/GaugeTag"; -import vehicleTrack from "assets/svg/vehicle-track.svg"; +import styles from './VCU.module.scss'; +import { Window } from 'components/Window/Window'; +import { useMeasurementsStore, VcuMeasurements } from 'common'; +import { GaugeTag } from 'components/GaugeTag/GaugeTag'; +import { IndicatorStack } from 'components/IndicatorStack/IndicatorStack'; +import { BarIndicator } from 'components/BarIndicator/BarIndicator'; +import positionIcon from 'assets/svg/z-index.svg'; +import { TrackVisualizer } from './TrackVisualizer/TrackVisualizer'; export const VCUPositionInfo = () => { - - const getNumericMeasurementInfo = useMeasurementsStore(state => state.getNumericMeasurementInfo); + const getNumericMeasurementInfo = useMeasurementsStore( + (state) => state.getNumericMeasurementInfo + ); const speed = getNumericMeasurementInfo(VcuMeasurements.speed); - const acceleration = getNumericMeasurementInfo(VcuMeasurements.acceleration); + const acceleration = getNumericMeasurementInfo( + VcuMeasurements.acceleration + ); + const position = getNumericMeasurementInfo(VcuMeasurements.position); return ( <Window title="VCU"> <div className={styles.vcuPositionInfo}> + <IndicatorStack> + <BarIndicator + title="Position" + icon={positionIcon} + getValue={position.getUpdate} + safeRangeMin={position.range[0]!!} + safeRangeMax={position.range[1]!!} + warningRangeMin={position.warningRange[0]!!} + warningRangeMax={position.warningRange[1]!!} + units="m" + /> + </IndicatorStack> <div className={styles.row}> - <GaugeTag + <GaugeTag + id="vcu_position_speed" name={speed.name} units={speed.units} getUpdate={speed.getUpdate} strokeWidth={120} - min={speed.range[0] || 0} - max={speed.range[1] || 50} + min={speed.warningRange[0] || 0} + max={speed.warningRange[1] || 50} /> - <GaugeTag + <GaugeTag + id="vcu_position_acceleration" name={acceleration.name} units={acceleration.units} getUpdate={acceleration.getUpdate} strokeWidth={120} - min={acceleration.range[0] || 0} - max={acceleration.range[1] || 50} + min={acceleration.warningRange[0] || 0} + max={acceleration.warningRange[1] || 19.6} /> </div> - <div className={styles.trackContainer}> - <img src={vehicleTrack} alt="Vehicle Track" /> - </div> + <TrackVisualizer + getUpdate={position.getUpdate} + rangeMin={position.warningRange[0] || 0} + rangeMax={position.warningRange[1] || 50} + /> </div> </Window> ); diff --git a/control-station/src/pages/VehiclePage/BoardsPage/BoardsPage.module.scss b/control-station/src/pages/VehiclePage/BoardsPage/BoardsPage.module.scss deleted file mode 100644 index 8beb9be5a..000000000 --- a/control-station/src/pages/VehiclePage/BoardsPage/BoardsPage.module.scss +++ /dev/null @@ -1,11 +0,0 @@ -.boardsPage { - width: 100%; - display: flex; - justify-content: center; - flex: 1; - gap: 1rem; - - > * { - min-width: fit-content; - } -} \ No newline at end of file diff --git a/control-station/src/pages/VehiclePage/BoardsPage/BoardsPage1.tsx b/control-station/src/pages/VehiclePage/BoardsPage/BoardsPage1.tsx deleted file mode 100644 index 159daaabb..000000000 --- a/control-station/src/pages/VehiclePage/BoardsPage/BoardsPage1.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import styles from "./BoardsPage.module.scss"; -import { OBCCUBatteries } from "../Boards/OBCCU/OBCCUBatteries"; -import { - useMeasurementsStore, - useSubscribe, -} from "common"; -import { OBCCUGeneralInfo } from "../Boards/OBCCU/OBCCUGeneralInfo"; -import { BMSL } from "../Boards/BMSL/BMSL"; -import { VCUPositionInfo } from "../Boards/VCU/VCUPositionInfo"; -import { VCUBrakesInfo } from "../Boards/VCU/VCUBrakesInfo"; - -export const BoardsPage1 = () => { - const updateMeasurements = useMeasurementsStore(state => state.updateMeasurements); - - useSubscribe("podData/update", (msg) => { - updateMeasurements(msg); - }); - - return ( - <div className={styles.boardsPage}> - <OBCCUBatteries /> - <div className={styles.column}> - <OBCCUGeneralInfo /> - <BMSL /> - </div> - <VCUPositionInfo /> - <VCUBrakesInfo /> - </div> - ); -}; diff --git a/control-station/src/pages/VehiclePage/BoardsPage/BoardsPage2.tsx b/control-station/src/pages/VehiclePage/BoardsPage/BoardsPage2.tsx deleted file mode 100644 index 05d84a2e4..000000000 --- a/control-station/src/pages/VehiclePage/BoardsPage/BoardsPage2.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import styles from "./BoardsPage.module.scss"; -import { LCU } from "../Boards/LCU/LCU"; -import { DLIM } from "../Boards/DLIM/DLIM"; -import { LSM } from "../Boards/LSM/LSM"; -import { Messages } from "../Messages/Messages"; - -export const BoardsPage2 = () => { - - return ( - <div className={styles.boardsPage}> - <LCU /> - - <div className={styles.column}> - <DLIM /> - <LSM /> - </div> - - <Messages /> - </div> - ); -}; diff --git a/control-station/src/pages/VehiclePage/ControlPage/ControlPage.module.scss b/control-station/src/pages/VehiclePage/ControlPage/ControlPage.module.scss deleted file mode 100644 index 49ef8c04d..000000000 --- a/control-station/src/pages/VehiclePage/ControlPage/ControlPage.module.scss +++ /dev/null @@ -1,42 +0,0 @@ -.controlPage { - display: grid; - grid-template: - "orders column connections" minmax(5rem, 1fr) - "orders column logger" min-content - "orders column emergencyOrders" auto / 1fr 1fr 1fr; - justify-items: stretch; - align-items: center; - gap: 1.3rem; - min-height: 0; - max-height: 100%; - font-size: 70%; -} - -.column { - display: grid; - grid-template-rows: 1fr auto; - align-self: stretch; - gap: 1.5rem; -} - -.controlPage > :nth-child(1) { - grid-area: orders; -} - -.controlPage > :nth-child(2) { - justify-self: stretch; - grid-area: column; -} - -.controlPage > :nth-child(3) { - grid-area: connections; -} - -.controlPage > :nth-child(4) { - grid-area: logger; -} - -.controlPage > :nth-child(5) { - justify-self: stretch; - grid-area: emergencyOrders; -} \ No newline at end of file diff --git a/control-station/src/pages/VehiclePage/ControlPage/ControlPage.tsx b/control-station/src/pages/VehiclePage/ControlPage/ControlPage.tsx deleted file mode 100644 index 7a21f8842..000000000 --- a/control-station/src/pages/VehiclePage/ControlPage/ControlPage.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { Order, Orders, useOrders, useSendOrder } from "common"; -import styles from "./ControlPage.module.scss"; -import { Connections, Logger, MessagesContainer } from "common"; -import { Window } from "components/Window/Window"; -import { EmergencyOrders } from "components/EmergencyOrders/EmergencyOrders"; -import { - BrakeOrder, - OpenContactorsOrder, - ResetVehicleOrder, - StopOrder, - getHardcodedOrders, -} from "./hardcodedOrders"; -import { BootloaderContainer } from "components/BootloaderContainer/BootloaderContainer"; - -export const ControlPage = () => { - const sendOrder = useSendOrder(); - const boardOrders = useOrders(); - - return ( - <div className={styles.controlPage}> - <Window - title="Orders" - height="fill" - > - <Orders orders={getHardcodedOrders(boardOrders)} /> - </Window> - <div className={styles.column}> - <Window - title="Messages" - height="fill" - > - <MessagesContainer /> - </Window> - <BootloaderContainer /> - </div> - <Window - title="Connections" - height="fill" - > - <Connections /> - </Window> - <Window title="Logger"> - <Logger /> - </Window> - <EmergencyOrders - brake={() => { - sendOrder(BrakeOrder); - }} - openContactors={() => { - sendOrder(OpenContactorsOrder); - }} - reset={() => { - sendOrder(ResetVehicleOrder); - }} - stop={() => { - sendOrder(StopOrder); - }} - /> - </div> - ); -}; diff --git a/control-station/src/pages/VehiclePage/ControlPage/hardcodedOrders.ts b/control-station/src/pages/VehiclePage/ControlPage/hardcodedOrders.ts deleted file mode 100644 index c34a4e0fa..000000000 --- a/control-station/src/pages/VehiclePage/ControlPage/hardcodedOrders.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { BoardOrders, Order, OrderDescription } from "common"; - -export const hardcodedOrderToId = { - set_regulator_pressure: 210, - brake: 215, - unbrake: 216, - disable_emergency_tape: 217, - enable_emergency_tape: 218, - test_current_control: 335, - open_contactors: 902, - close_contactors: 903, - take_off: 300, - test_svppwm: 615, - stop_lcu_control: 316, - stop_pcu_control: 609, -}; - -export function getHardcodedOrders(boardOrders: BoardOrders[]): BoardOrders[] { - const foundOrders = [] as OrderDescription[]; - const wantedOrdersIds = Object.values(hardcodedOrderToId); - for (const board of boardOrders) { - for (const order of board.orders) { - if (wantedOrdersIds.includes(order.id)) { - foundOrders.push(order); - } - } - for (const stateOrder of board.stateOrders) { - if (wantedOrdersIds.includes(stateOrder.id)) { - foundOrders.push(stateOrder); - } - } - } - - return [{ name: "", orders: foundOrders, stateOrders: [] }]; -} - -export const hardcodedOrders: BoardOrders[] = [ - { - name: "", - orders: [ - { - id: 210, - name: "Set reference pressure", - fields: { - new_reference_pressure: { - id: "new_reference_pressure", - kind: "numeric", - name: "New reference pressure", - safeRange: [0, 10], - warningRange: [0, 10], - type: "float32", - }, - }, - }, - { id: 215, name: "Brake", fields: {} }, - { id: 216, name: "Unbrake", fields: {} }, - { id: 217, name: "Disable emergency tape", fields: {} }, - { id: 218, name: "Enable emergency tape", fields: {} }, - - { id: 335, name: "Test current control", fields: {} }, - // { id: 217, name: "Disable emergency tape", fields: {} }, - // { id: 218, name: "Enable emergency tape", fields: {} }, - // { id: 215, name: "Brake", fields: {} }, - // { id: 216, name: "Unbrake", fields: {} }, - // { id: 210, name: "Set reference pressure", fields: {} }, - // { id: 200, name: "Emergency stop", fields: {} }, - // { id: 209, name: "Reset VCU", fields: {} }, - // { id: 316, name: "Stop LCU control", fields: {} }, - // { id: 326, name: "Reset all LCUs", fields: {} }, - ], - stateOrders: [], - }, -]; - -export const ResetVehicleOrder: Order = { - id: 250, - fields: {}, -}; - -export const StopOrder: Order = { - id: 200, - fields: {}, -}; - -export const BrakeOrder: Order = { - id: 215, - fields: {}, -}; - -export const OpenContactorsOrder: Order = { - id: 902, - fields: {}, -}; diff --git a/control-station/src/pages/VehiclePage/Boards/LSM/LSM.module.scss b/control-station/src/pages/VehiclePage/Data1Page/Data1Page.module.scss similarity index 53% rename from control-station/src/pages/VehiclePage/Boards/LSM/LSM.module.scss rename to control-station/src/pages/VehiclePage/Data1Page/Data1Page.module.scss index 1a17de6d9..83470e8f7 100644 --- a/control-station/src/pages/VehiclePage/Boards/LSM/LSM.module.scss +++ b/control-station/src/pages/VehiclePage/Data1Page/Data1Page.module.scss @@ -1,11 +1,11 @@ -.LSMWrapper { +.data1_page { display: flex; - flex-direction: column; + flex-flow: row wrap; gap: 1rem; } .column { display: flex; - flex-direction: column; + flex-flow: column; gap: 1rem; -} \ No newline at end of file +} diff --git a/control-station/src/pages/VehiclePage/Data1Page/Data1Page.tsx b/control-station/src/pages/VehiclePage/Data1Page/Data1Page.tsx new file mode 100644 index 000000000..bcbf1a1e4 --- /dev/null +++ b/control-station/src/pages/VehiclePage/Data1Page/Data1Page.tsx @@ -0,0 +1,36 @@ +import styles from './Data1Page.module.scss'; +import { OBCCUBatteries } from '../Boards/OBCCU/OBCCUBatteries'; +import { OBCCUGeneralInfo } from '../Boards/OBCCU/OBCCUGeneralInfo'; +import { VCUBrakesInfo } from '../Boards/VCU/VCUBrakesInfo'; +import { VCUPositionInfo } from '../Boards/VCU/VCUPositionInfo'; +import { VCUConnectionsInfo } from '../Boards/VCU/VCUConnectionsInfo'; +import { BCU } from '../Boards/BCU/BCU'; +import { BMSL } from '../Boards/BMSL/BMSL'; + +export const Data1Page = () => { + return ( + <div className={styles.data1_page}> + <div className={styles.column}> + <OBCCUBatteries /> + </div> + + <div className={styles.column}> + <OBCCUGeneralInfo /> + <BMSL /> + </div> + + <div className={styles.column}> + <VCUPositionInfo /> + </div> + + <div className={styles.column}> + <VCUBrakesInfo /> + <VCUConnectionsInfo /> + </div> + + <div className={styles.column}> + <BCU /> + </div> + </div> + ); +}; diff --git a/control-station/src/pages/VehiclePage/Data2Page/Data2Page.module.scss b/control-station/src/pages/VehiclePage/Data2Page/Data2Page.module.scss new file mode 100644 index 000000000..349926b0a --- /dev/null +++ b/control-station/src/pages/VehiclePage/Data2Page/Data2Page.module.scss @@ -0,0 +1,25 @@ +.data2_page { + display: flex; + flex-flow: row wrap; + gap: 1rem; +} + +.column { + display: flex; + flex-flow: column; + gap: 1rem; + height: fit-content; +} + +.orders { + max-height: 85vh; + width: 18vw; + max-width: 18vw; +} + +.messages { + height: 70vh; + max-height: 70vh; + width: 14vw; + max-width: 14vw; +} diff --git a/control-station/src/pages/VehiclePage/Data2Page/Data2Page.tsx b/control-station/src/pages/VehiclePage/Data2Page/Data2Page.tsx new file mode 100644 index 000000000..1a9246fc1 --- /dev/null +++ b/control-station/src/pages/VehiclePage/Data2Page/Data2Page.tsx @@ -0,0 +1,43 @@ +import styles from './Data2Page.module.scss'; +import { LCU } from '../Boards/LCU/LCU'; +import { PCU } from '../Boards/PCU/PCU'; +import { Orders, useOrders } from 'common'; +import { Connections, Logger, MessagesContainer } from 'common'; +import { Window } from 'components/Window/Window'; +import { getHardcodedOrders } from './hardcodedOrders'; + +export const Data2Page = () => { + const boardOrders = useOrders(); + + return ( + <div className={styles.data2_page}> + <div className={`${styles.column} ${styles.lcu}`}> + <LCU /> + </div> + + <div className={styles.column}> + <PCU /> + </div> + + <div className={styles.column}> + <Window title="Orders" className={styles.orders}> + <Orders boards={getHardcodedOrders(boardOrders)} /> + </Window> + + <Window title="Logger"> + <Logger /> + </Window> + </div> + + <div className={styles.column}> + <Window title="Messages" className={styles.messages}> + <MessagesContainer /> + </Window> + + <Window title="Connections"> + <Connections /> + </Window> + </div> + </div> + ); +}; diff --git a/control-station/src/pages/VehiclePage/Data2Page/hardcodedOrders.ts b/control-station/src/pages/VehiclePage/Data2Page/hardcodedOrders.ts new file mode 100644 index 000000000..2b77e2183 --- /dev/null +++ b/control-station/src/pages/VehiclePage/Data2Page/hardcodedOrders.ts @@ -0,0 +1,57 @@ +import { BoardOrders, Order, OrderDescription } from 'common'; + +export const hardcodedOrderToId = { + set_regulator_pressure: 210, + brake: 215, + unbrake: 216, + disable_emergency_tape: 217, + enable_emergency_tape: 218, + start_vertical_levitation: 356, + stop_levitation: 357, + start_horizontal_levitation: 360, + test_current_control: 607, + stop_pcu_control: 609, + test_speed_control: 619, + test_svpwm: 615, + open_contactors: 902, + close_contactors: 903, +}; + +export function getHardcodedOrders(boardOrders: BoardOrders[]): BoardOrders[] { + const foundOrders = [] as OrderDescription[]; + const wantedOrdersIds = Object.values(hardcodedOrderToId); + for (const board of boardOrders) { + for (const order of board.orders) { + if (wantedOrdersIds.includes(order.id)) { + foundOrders.push(order); + } + } + for (const stateOrder of board.stateOrders) { + if (wantedOrdersIds.includes(stateOrder.id)) { + foundOrders.push(stateOrder); + } + } + } + + return [{ name: 'General Orders', orders: foundOrders, stateOrders: [] }]; +} + +export const ResetVehicleOrder: Order = { + id: 250, + fields: {}, +}; + +export const StopOrder: Order = { + id: 200, + fields: {}, +}; + +export const BrakeOrder: Order = { + id: 215, + fields: {}, +}; + +export const OpenContactorsOrder: Order = { + id: 902, + fields: {}, +}; diff --git a/control-station/src/pages/VehiclePage/Messages/Messages.tsx b/control-station/src/pages/VehiclePage/Messages/Messages.tsx index dea0a26f0..5ef5c1b55 100644 --- a/control-station/src/pages/VehiclePage/Messages/Messages.tsx +++ b/control-station/src/pages/VehiclePage/Messages/Messages.tsx @@ -1,10 +1,10 @@ -import { MessagesContainer } from "common" -import { Window } from "components/Window/Window" +import { MessagesContainer } from 'common'; +import { Window } from 'components/Window/Window'; export const Messages = () => { return ( - <Window title="Messages" height="fill"> + <Window title="Messages"> <MessagesContainer /> </Window> - ) -} + ); +}; diff --git a/control-station/src/pages/VehiclePage/VehiclePage.module.scss b/control-station/src/pages/VehiclePage/VehiclePage.module.scss index e69de29bb..1078cbe8a 100644 --- a/control-station/src/pages/VehiclePage/VehiclePage.module.scss +++ b/control-station/src/pages/VehiclePage/VehiclePage.module.scss @@ -0,0 +1,7 @@ +.pagination_position { + position: fixed; + right: 50%; + bottom: 1rem; + + width: fit-content; +} diff --git a/control-station/src/pages/VehiclePage/VehiclePage.tsx b/control-station/src/pages/VehiclePage/VehiclePage.tsx index b1dd9dcda..e26e34799 100644 --- a/control-station/src/pages/VehiclePage/VehiclePage.tsx +++ b/control-station/src/pages/VehiclePage/VehiclePage.tsx @@ -1,45 +1,20 @@ -import { useGlobalTicker, useMeasurementsStore, useSubscribe } from "common"; -import styles from "./VehiclePage.module.scss"; - -import { Pagination } from "components/Pagination/Pagination"; -import { PageWrapper } from "pages/PageWrapper/PageWrapper"; -import { Outlet } from "react-router-dom"; -import { useOrders } from "useOrders"; -import { fetchFromBackend } from "services/HTTPHandler"; -import { useEffect, useState } from "react"; +import styles from './VehiclePage.module.scss'; +import { Pagination } from 'components/Pagination/Pagination'; +import { PageWrapper } from 'pages/PageWrapper/PageWrapper'; +import { Outlet } from 'react-router-dom'; +import { useEmergencyOrders } from 'hooks/useEmergencyOrders'; +import { usePodDataUpdate } from 'hooks/usePodDataUpdate'; export const VehiclePage = () => { - - const [podData, setPodData] = useState(null); - const initMeasurements = useMeasurementsStore(state => state.initMeasurements); - - useEffect(() => { - const fetchPodDataAsync = async () => { - const data = await fetchPodData(); - setPodData(data); - }; - fetchPodDataAsync(); - }, []); - - useEffect(() => { - if (podData) { - initMeasurements(podData); - } - }, [podData, initMeasurements]); - - useOrders(); + usePodDataUpdate(); + // useEmergencyOrders(); return ( <PageWrapper title="Vehicle"> <Outlet /> - <Pagination routes={["first", "second", "thirst"]} /> + <div className={styles.pagination_position}> + <Pagination routes={['data-1', 'data-2']} /> + </div> </PageWrapper> ); }; - -async function fetchPodData() { - const response = await fetchFromBackend( - import.meta.env.VITE_POD_DATA_DESCRIPTION_PATH - ); - return response.json(); -} \ No newline at end of file diff --git a/control-station/src/pages/VehiclePage/vehicleRoute.tsx b/control-station/src/pages/VehiclePage/vehicleRoute.tsx index f92f71c7e..8fa85d7ff 100644 --- a/control-station/src/pages/VehiclePage/vehicleRoute.tsx +++ b/control-station/src/pages/VehiclePage/vehicleRoute.tsx @@ -1,16 +1,14 @@ -import { BoardsPage1 } from "./BoardsPage/BoardsPage1"; -import { BoardsPage2 } from "./BoardsPage/BoardsPage2"; -import { ControlPage } from "./ControlPage/ControlPage"; -import { VehiclePage } from "./VehiclePage"; -import { Navigate } from "react-router-dom"; +import { Data1Page } from './Data1Page/Data1Page'; +import { Data2Page } from './Data2Page/Data2Page'; +import { VehiclePage } from './VehiclePage'; +import { Navigate } from 'react-router-dom'; export const vehicleRoute = { - path: "/vehicle", + path: '/vehicle', element: <VehiclePage />, children: [ - { path: "", element: <Navigate to={"first"} /> }, - { path: "first", element: <BoardsPage1 /> }, - { path: "second", element: <BoardsPage2 /> }, - { path: "thirst", element: <ControlPage /> }, + { path: '', element: <Navigate to={'data-1'} /> }, + { path: 'data-1', element: <Data1Page /> }, + { path: 'data-2', element: <Data2Page /> }, ], }; diff --git a/control-station/src/state.ts b/control-station/src/state.ts index 9720cc0de..87efdc116 100644 --- a/control-station/src/state.ts +++ b/control-station/src/state.ts @@ -1,29 +1,68 @@ import { EnumMeasurement, Measurement, + ObccuMeasurements, + PcuMeasurements, + VcuMeasurements, clamp, clampAndNormalize, isNumericMeasurement, -} from "common"; +} from 'common'; -export type State = "stable" | "warning" | "fault"; - -const FaultLowerBound = 20; -const FaultUpperBound = 80; - -const WarningLowerBound = 40; -const WarningUpperBound = 60; +export type State = 'stable' | 'warning' | 'fault' | 'ignore'; export const stateToColor = { - stable: "#ACF293", - warning: "#F4F688", - fault: "#EF9A87", + stable: '#ACF293', + warning: '#F4F688', + fault: '#EF9A87', + ignore: '#EDF6FE', }; export const stateToColorBackground = { - stable: "#E6FFDD", - warning: "#FCFFDD", - fault: "#FFE5DD", + stable: '#E6FFDD', + warning: '#FCFFDD', + fault: '#FFE5DD', + ignore: '#EDF6FE', +}; + +const enumStates: { [meas_id: string]: { [enum_variant: string]: State } } = { + [VcuMeasurements.valveState]: { + OPEN: 'warning', + }, + [VcuMeasurements.pcuConnection]: { + PCU_Connected: 'stable', + }, + [VcuMeasurements.obccuConnection]: { + OBCCU_Connected: 'stable', + }, + [VcuMeasurements.lcuConnection]: { + LCU_Connected: 'stable', + }, + [ObccuMeasurements.contactorsState]: { + OPEN: 'stable', + PRECHARGE: 'warning', + CLOSED: 'warning', + }, + [ObccuMeasurements.imdState]: { + DEVICE_ERROR: 'fault', + ISOLATED: 'stable', + UNKNOWN: 'warning', + DRIFT: 'fault', + EARTH_FAULT: 'fault', + SHORT_CIRCUIT: 'fault', + }, + [ObccuMeasurements.generalState]: { + FAULT: 'fault', + OPERATIONAL: 'stable', + }, + [PcuMeasurements.generalState]: { + FAULT: 'fault', + OPERATIONAL: 'stable', + }, + [VcuMeasurements.generalState]: { + FAULT: 'fault', + OPERATIONAL: 'stable', + }, }; export function getState(meas: Measurement): State { @@ -31,51 +70,58 @@ export function getState(meas: Measurement): State { return getStateFromRange( meas.value.last, meas.safeRange[0], - meas.safeRange[1] + meas.safeRange[1], + meas.warningRange[0], + meas.warningRange[1] ); - } else if (meas.type == "bool") { - return meas.value ? "stable" : "fault"; + } else if (meas.type == 'bool') { + return meas.value ? 'stable' : 'fault'; } else { - return "stable"; + if ( + enumStates[meas.id] != undefined && + enumStates[meas.id][meas.value] != undefined + ) { + return enumStates[meas.id][meas.value]; + } + return 'ignore'; } } export function getStateFromEnum(_: EnumMeasurement): State { - return "stable"; + return 'stable'; } export function getStateFromRange( value: number, - min: number | null, - max: number | null + safeMin: number | null, + safeMax: number | null, + warningMin: number | null, + warningMax: number | null ): State { - if (min !== null && max !== null) { - const percentage = clampAndNormalize(value, min, max) * 100; + if (warningMin !== null && value < warningMin) { + return 'fault'; + } - if (percentage < FaultLowerBound || percentage > FaultUpperBound) { - return "fault"; - } else if ( - percentage < WarningLowerBound || - percentage > WarningUpperBound - ) { - return "warning"; - } else { - return "stable"; - } + if (safeMin !== null && value < safeMin) { + return 'warning'; } - if ((min !== null && value > min) || (max !== null && value < max)) { - return "stable"; + if (warningMax !== null && value > warningMax) { + return 'fault'; } - if (min === null && max === null) { - return "stable"; + if (safeMax !== null && value > safeMax) { + return 'warning'; } - return "fault"; + return 'stable'; } -export function getPercentageFromRange(value: number, min: number, max: number): number { +export function getPercentageFromRange( + value: number, + min: number, + max: number +): number { const normValue = Math.max(Math.min(value, max), min); return ((normValue - min) / (max - min)) * 100; -} \ No newline at end of file +} diff --git a/control-station/src/styles/colors.scss b/control-station/src/styles/colors.scss index 4c54970e1..a22b89a19 100644 --- a/control-station/src/styles/colors.scss +++ b/control-station/src/styles/colors.scss @@ -1,4 +1,4 @@ -@use "sass:color"; +@use 'sass:color'; $key-colors: ( primary: hsl(189, 25%, 40%), @@ -15,10 +15,10 @@ $lightnesses: 0, 10, 15, 20, 30, 40, 50, 60, 70, 80, 85, 90, 95, 99, 100; @mixin globalColors { @each $name, $color in $key-colors { @each $lightness in $lightnesses { - --color-#{$name}-#{$lightness}: hsl( + --color-#{"" + $name}-#{$lightness}: hsl( #{color.hue($color)}, #{color.saturation($color)}, - #{$lightness}#{"%"} + #{$lightness}#{'%'} ); } } diff --git a/ethernet-view/src/components/SplashScreen/SplashScreen.tsx b/ethernet-view/src/components/SplashScreen/SplashScreen.tsx index d636e5c2b..c40ef6101 100644 --- a/ethernet-view/src/components/SplashScreen/SplashScreen.tsx +++ b/ethernet-view/src/components/SplashScreen/SplashScreen.tsx @@ -1,10 +1,11 @@ -import styles from "./SplashScreen.module.scss"; -import { animated, useSpring } from "@react-spring/web"; +import styles from './SplashScreen.module.scss'; +import { animated, useSpring } from '@react-spring/web'; +// TODO: change for common front SplashScreen export const SplashScreen = () => { const springs = useSpring({ - from: { fontSize: "0rem" }, - to: { fontSize: "16rem" }, + from: { fontSize: '0rem' }, + to: { fontSize: '16rem' }, config: { mass: 5, }, @@ -13,10 +14,7 @@ export const SplashScreen = () => { return ( <div className={styles.loadingView}> - <animated.div - className={styles.monkey} - style={{ ...springs }} - > + <animated.div className={styles.monkey} style={{ ...springs }}> 🐒 </animated.div> </div> diff --git a/packet-sender/PacketGenerator.go b/packet-sender/PacketGenerator.go index b12c28287..53693aaf2 100644 --- a/packet-sender/PacketGenerator.go +++ b/packet-sender/PacketGenerator.go @@ -74,28 +74,35 @@ func New() PacketGenerator { return pg } -// func (pg *PacketGenerator) CreateRandomPacket() []byte { -// randomIndex := rand.Int63n(int64(len(pg.packets))) -// randomPacket := pg.packets[randomIndex] +func (pg *PacketGenerator) CreateRandomPacket() []byte { + randomIndex := rand.Int63n(int64(len(pg.packets))) + randomPacket := pg.packets[randomIndex] -// buff := bytes.NewBuffer(make([]byte, 0)) + buff := bytes.NewBuffer(make([]byte, 0)) -// binary.Write(buff, binary.LittleEndian, randomPacket.ID) + binary.Write(buff, binary.LittleEndian, randomPacket.ID) -// for _, measurement := range randomPacket.Measurements { -// if strings.Contains(measurement.Type, "enum") { -// binary.Write(buff, binary.LittleEndian, uint8(1)) -// } else if measurement.Type != "string" { -// number := mapNumberToRange(rand.Float64(), measurement.SafeRange, measurement.Type) -// writeNumberAsBytes(number, measurement.Type, buff) -// } else { -// return nil -// } + for _, measurement := range randomPacket.Measurements { + if strings.Contains(measurement.Type, "enum") { + binary.Write(buff, binary.LittleEndian, uint8(rand.Int63n(int64(len(strings.Split(strings.ReplaceAll(strings.TrimSuffix(strings.TrimPrefix(measurement.Type, "enum("), ")"), " ", ""), ",")))))) + } else if measurement.Type == "bool" { + binary.Write(buff, binary.LittleEndian, rand.Int31n(2) == 1) + } else if measurement.Type != "string" { + var number float64 + if len(measurement.WarningRange) == 0 { + number = mapNumberToRange(rand.Float64(), measurement.WarningRange, measurement.Type) + } else { + number = mapNumberToRange(rand.Float64(), []float64{measurement.WarningRange[0] * 0.8, measurement.WarningRange[1] * 1.2}, measurement.Type) + } + writeNumberAsBytes(number, measurement.Type, buff) + } else { + return nil + } -// } + } -// return buff.Bytes() -// } + return buff.Bytes() +} func (pg *PacketGenerator) CreateSinePacket() []byte { randomIndex := rand.Int63n(int64(len(pg.packets))) diff --git a/packet-sender/main.go b/packet-sender/main.go index 335354614..4c5abcc19 100644 --- a/packet-sender/main.go +++ b/packet-sender/main.go @@ -33,13 +33,11 @@ func main() { fmt.Println("Sending packets") count := make(chan struct{}, 10000) - ticker := time.NewTicker(time.Millisecond * 1) start := time.Now() prev := time.Now() go func() { - for range ticker.C { - packet := packetGenerator.CreateSinePacket() - // packet := []byte{10, 0, 20, 0, 20, 0, 20, 0, 20, 0} + for { + packet := packetGenerator.CreateRandomPacket() fmt.Println(time.Since(prev)) prev = time.Now()