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 { useDevice } from "@core/stores/deviceStore.ts";
import { Protobuf } from "@meshtastic/js";
import { useCallback } from "react";
export const Position = () => {
const { config, setWorkingConfig } = useDevice();
const { flagsValue, activeFlags, toggleFlag } = usePositionFlags(
const { flagsValue, activeFlags, toggleFlag, getAllFlags } = usePositionFlags(
config.position.positionFlags ?? 0,
);
@ -24,9 +25,12 @@ export const Position = () => {
);
};
const onPositonFlagChange = (name: string) => {
return toggleFlag(name as FlagName);
};
const onPositonFlagChange = useCallback(
(name: string) => {
return toggleFlag(name as FlagName);
},
[toggleFlag],
);
return (
<DynamicForm<PositionValidation>
@ -73,9 +77,10 @@ export const Position = () => {
onValueChange: onPositonFlagChange,
label: "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: {
enumValue: Protobuf.Config.Config_PositionConfig_PositionFlags,
enumValue: getAllFlags(),
},
},
{

4
src/components/UI/MultiSelect.tsx

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

80
src/core/hooks/usePositionFlags.ts

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

Loading…
Cancel
Save