Browse Source

fix: updating position flags to be more readable

pull/394/head
Dan Ditomaso 1 year ago
parent
commit
53fe300fe9
  1. 17
      src/components/PageComponents/Config/Position.tsx
  2. 4
      src/components/UI/MultiSelect.tsx
  3. 80
      src/core/hooks/usePositionFlags.ts

17
src/components/PageComponents/Config/Position.tsx

@ -6,10 +6,11 @@ import type { PositionValidation } from "@app/validation/config/position.tsx";
import { DynamicForm } from "@components/Form/DynamicForm.tsx"; import { DynamicForm } from "@components/Form/DynamicForm.tsx";
import { useDevice } from "@core/stores/deviceStore.ts"; import { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js"; import { Protobuf } from "@meshtastic/js";
import { useCallback } from "react";
export const Position = () => { export const Position = () => {
const { config, setWorkingConfig } = useDevice(); const { config, setWorkingConfig } = useDevice();
const { flagsValue, activeFlags, toggleFlag } = usePositionFlags( const { flagsValue, activeFlags, toggleFlag, getAllFlags } = usePositionFlags(
config.position.positionFlags ?? 0, config.position.positionFlags ?? 0,
); );
@ -24,9 +25,12 @@ export const Position = () => {
); );
}; };
const onPositonFlagChange = (name: string) => { const onPositonFlagChange = useCallback(
return toggleFlag(name as FlagName); (name: string) => {
}; return toggleFlag(name as FlagName);
},
[toggleFlag],
);
return ( return (
<DynamicForm<PositionValidation> <DynamicForm<PositionValidation>
@ -73,9 +77,10 @@ export const Position = () => {
onValueChange: onPositonFlagChange, onValueChange: onPositonFlagChange,
label: "Position Flags", label: "Position Flags",
placeholder: "Select position flags...", placeholder: "Select position flags...",
description: "Configuration options for Position messages", description:
"Optional fields to include when assembling position messages. The more fields are selected, the larger the message will be leading to longer airtime usage and a higher risk of packet loss.",
properties: { properties: {
enumValue: Protobuf.Config.Config_PositionConfig_PositionFlags, enumValue: getAllFlags(),
}, },
}, },
{ {

4
src/components/UI/MultiSelect.tsx

@ -46,10 +46,10 @@ const MultiSelectItem = ({
className, className,
)} )}
> >
<CheckboxPrimitive.Indicator> <CheckboxPrimitive.Indicator className="mr-2">
<Check className="h-4 w-4 animate-in zoom-in duration-200" /> <Check className="h-4 w-4 animate-in zoom-in duration-200" />
</CheckboxPrimitive.Indicator> </CheckboxPrimitive.Indicator>
<span className="ml-2">{children}</span> {children}
</CheckboxPrimitive.Root> </CheckboxPrimitive.Root>
); );
}; };

80
src/core/hooks/usePositionFlags.ts

@ -1,23 +1,27 @@
import { useCallback, useMemo, useState } from "react"; import { useCallback, useMemo, useState } from "react";
export type FlagName = const FLAGS = {
| "UNSET" UNSET: 0,
| "ALTITUDE" Altitude: 1,
| "ALTITUDE_MSL" "Altitude is Mean Sea Level": 2,
| "GEOIDAL_SEPARATION" "Altitude Geoidal Seperation": 4,
| "DOP" "Dilution of precision (DOP) PDOP used by default": 8,
| "HVDOP" "If DOP is set, use HDOP / VDOP values instead of PDOP": 16,
| "SATINVIEW" "Number of satellites": 32,
| "SEQ_NO" "Sequence number": 64,
| "TIMESTAMP" Timestamp: 128,
| "HEADING" "Vehicle heading": 256,
| "SPEED"; "Vehicle speed": 512,
} as const;
export type FlagName = keyof typeof FLAGS;
type FlagsObject = typeof FLAGS;
type UsePositionFlagsProps = { type UsePositionFlagsProps = {
decode: (value: number) => FlagName[]; decode: (value: number) => FlagName[];
encode: (flagNames: FlagName[]) => number; encode: (flagNames: FlagName[]) => number;
hasFlag: (value: number, flagName: FlagName) => boolean; hasFlag: (value: number, flagName: FlagName) => boolean;
getAllFlags: () => FlagName[]; getAllFlags: () => FlagsObject;
isValidValue: (value: number) => boolean; isValidValue: (value: number) => boolean;
flagsValue: number; flagsValue: number;
activeFlags: FlagName[]; activeFlags: FlagName[];
@ -27,30 +31,17 @@ type UsePositionFlagsProps = {
clearFlags: () => void; clearFlags: () => void;
}; };
const FLAGS_MAP: ReadonlyMap<FlagName, number> = new Map([
["UNSET", 0],
["ALTITUDE", 1],
["ALTITUDE_MSL", 2],
["GEOIDAL_SEPARATION", 4],
["DOP", 8],
["HVDOP", 16],
["SATINVIEW", 32],
["SEQ_NO", 64],
["TIMESTAMP", 128],
["HEADING", 256],
["SPEED", 512],
]);
export const usePositionFlags = (initialValue = 0): UsePositionFlagsProps => { export const usePositionFlags = (initialValue = 0): UsePositionFlagsProps => {
const [flagsValue, setFlagsValue] = useState<number>(initialValue); const [flagsValue, setFlagsValue] = useState<number>(initialValue);
const utils = useMemo(() => { const utils = useMemo(() => {
const decode = (value: number): FlagName[] => { const decode = (value: number): FlagName[] => {
if (value === 0) return ["UNSET"]; if (value === 0) return ["UNSET"];
const activeFlags: FlagName[] = []; const activeFlags: FlagName[] = [];
for (const [name, flagValue] of FLAGS_MAP) { for (const [name, flagValue] of Object.entries(FLAGS)) {
if (flagValue !== 0 && (value & flagValue) === flagValue) { if (flagValue !== 0 && (value & flagValue) === flagValue) {
activeFlags.push(name); activeFlags.push(name as FlagName);
} }
} }
return activeFlags; return activeFlags;
@ -61,31 +52,24 @@ export const usePositionFlags = (initialValue = 0): UsePositionFlagsProps => {
return 0; return 0;
} }
return flagNames.reduce((acc, name) => { return flagNames.reduce((acc, name) => {
const value = FLAGS_MAP.get(name); const value = FLAGS[name];
if (value === undefined) {
throw new Error(`Invalid flag name: ${name}`);
}
return acc | value; return acc | value;
}, 0); }, 0);
}; };
const hasFlag = (value: number, flagName: FlagName): boolean => { const hasFlag = (value: number, flagName: FlagName): boolean => {
const flagValue = FLAGS_MAP.get(flagName); const flagValue = FLAGS[flagName];
if (flagValue === undefined) {
throw new Error(`Invalid flag name: ${flagName}`);
}
return (value & flagValue) === flagValue; return (value & flagValue) === flagValue;
}; };
const getAllFlags = (): FlagName[] => { const getAllFlags = (): FlagsObject => {
return Array.from(FLAGS_MAP.keys()); return FLAGS;
}; };
const isValidValue = (value: number): boolean => { const isValidValue = (value: number): boolean => {
const maxValue = Array.from(FLAGS_MAP.values()).reduce( const maxValue = Object.values(FLAGS)
(a, b) => a + b, .filter((val) => val !== 0) // Exclude UNSET (0) from the calculation
0, .reduce((acc, val) => acc | val, 0);
);
return Number.isInteger(value) && value >= 0 && value <= maxValue; return Number.isInteger(value) && value >= 0 && value <= maxValue;
}; };
@ -99,18 +83,12 @@ export const usePositionFlags = (initialValue = 0): UsePositionFlagsProps => {
}, []); }, []);
const toggleFlag = useCallback((flagName: FlagName) => { const toggleFlag = useCallback((flagName: FlagName) => {
const flagValue = FLAGS_MAP.get(flagName); const flagValue = FLAGS[flagName];
if (flagValue === undefined) {
throw new Error(`Invalid flag name: ${flagName}`);
}
setFlagsValue((prev) => prev ^ flagValue); setFlagsValue((prev) => prev ^ flagValue);
}, []); }, []);
const setFlag = useCallback((flagName: FlagName, enabled: boolean) => { const setFlag = useCallback((flagName: FlagName, enabled: boolean) => {
const flagValue = FLAGS_MAP.get(flagName); const flagValue = FLAGS[flagName];
if (flagValue === undefined) {
throw new Error(`Invalid flag name: ${flagName}`);
}
setFlagsValue((prev) => (enabled ? prev | flagValue : prev & ~flagValue)); setFlagsValue((prev) => (enabled ? prev | flagValue : prev & ~flagValue));
}, []); }, []);

Loading…
Cancel
Save