Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support for Plugins #215

Merged
merged 24 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ docker-compose.yml
server/target
vrp_tests
server/debug_files/*
server/algorithms/src/routing/tsp
server/algorithms/src/**/plugins/**/*
!server/algorithms/src/**/plugins/.gitkeep

# misc
.idea/*
Expand Down
3 changes: 3 additions & 0 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,9 @@ export default function App() {
}
setStatic('scannerType', res.scanner_type)
setStatic('dangerous', res.dangerous || false)
setStatic('route_plugins', res.route_plugins || [])
setStatic('clustering_plugins', res.clustering_plugins || [])
setStatic('bootstrap_plugins', res.bootstrap_plugins || false)
if (!res.logged_in) {
router.navigate('/login')
}
Expand Down
8 changes: 4 additions & 4 deletions client/src/assets/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,9 @@ export const CLUSTERING_MODES = [

export const SORT_BY = [
'None',
'GeoHash',
'S2Cell',
'TSP',
'ClusterCount',
'Random',
'S2Cell',
'Geohash',
'LatLon',
'PointCount',
] as const
3 changes: 3 additions & 0 deletions client/src/assets/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,9 @@ export interface Config {
scanner_type: 'rdm' | 'unown' | 'hybrid'
logged_in: boolean
dangerous: boolean
route_plugins: string[]
clustering_plugins: string[]
bootstrap_plugins: string[]
}

export type CombinedState = Partial<UsePersist> & Partial<UseStatic>
Expand Down
4 changes: 2 additions & 2 deletions client/src/components/drawer/Drawing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { usePersist } from '@hooks/usePersist'
import ListSubheader from '../styled/Subheader'
import Toggle from './inputs/Toggle'
import { MultiOptionList } from './inputs/MultiOptions'
import NumInput from './inputs/NumInput'
import UserTextInput from './inputs/NumInput'
import { LineColorSelector } from './inputs/LineStringColor'

export default function DrawingTab() {
Expand All @@ -17,7 +17,7 @@ export default function DrawingTab() {
<ListSubheader disableGutters>Drawing</ListSubheader>
<Toggle field="snappable" />
<Toggle field="continueDrawing" />
<NumInput field="radius" disabled={calculationMode === 'S2'} />
<UserTextInput field="radius" disabled={calculationMode === 'S2'} />
<MultiOptionList
field="setActiveMode"
label="Activate"
Expand Down
68 changes: 58 additions & 10 deletions client/src/components/drawer/Routing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,18 @@ import { usePersist } from '@hooks/usePersist'
import { clusteringRouting } from '@services/fetches'

import ListSubheader from '../styled/Subheader'
import NumInput from './inputs/NumInput'
import UserTextInput from './inputs/NumInput'
import { MultiOptionList } from './inputs/MultiOptions'
import Toggle from './inputs/Toggle'

const formatPluginName = (item: string) => {
if (item === 'tsp') return 'TSP'
if (item.includes('.')) {
const [plugin, ext] = item.split('.')
return `${plugin} (${ext})`
}
return item
}
export default function RoutingTab() {
const mode = usePersist((s) => s.mode)
const category = usePersist((s) => s.category)
Expand All @@ -40,6 +48,25 @@ export default function RoutingTab() {
const isEditing = useStatic((s) =>
Object.values(s.layerEditing).some((v) => v),
)
const routePlugins = useStatic((s) => s.route_plugins)
const clusteringPlugins = useStatic((s) => s.clustering_plugins)
const bootstrapPlugins = useStatic((s) => s.bootstrap_plugins)

const sortByOptions = React.useMemo(() => {
return [...SORT_BY, ...routePlugins]
}, [routePlugins])
const clusterOptions = React.useMemo(() => {
return [...CLUSTERING_MODES, ...clusteringPlugins]
}, [clusteringPlugins])
const bootstrapOptions = React.useMemo(() => {
return [...CALC_MODE, ...bootstrapPlugins]
}, [bootstrapPlugins])

React.useEffect(() => {
if (!CALC_MODE.some((x) => x === calculation_mode)) {
usePersist.setState({ calculation_mode: 'Radius' })
}
}, [mode])

const fastest = cluster_mode === 'Fastest'
return (
Expand All @@ -59,13 +86,20 @@ export default function RoutingTab() {
</Collapse>
<MultiOptionList
field="calculation_mode"
buttons={CALC_MODE}
buttons={mode === 'bootstrap' ? bootstrapOptions : CALC_MODE}
label="Strategy"
hideLabel
type="select"
/>
<Collapse
in={
!CALC_MODE.some((x) => x === calculation_mode) && mode === 'bootstrap'
}
>
<UserTextInput field="bootstrapping_args" helperText="--x 1 --y abc" />
</Collapse>
<Collapse in={calculation_mode === 'Radius'}>
<NumInput field="radius" />
<UserTextInput field="radius" />
</Collapse>
<Collapse in={calculation_mode === 'S2'}>
<MultiOptionList
Expand All @@ -89,26 +123,40 @@ export default function RoutingTab() {
<Collapse in={mode !== 'bootstrap' && calculation_mode === 'Radius'}>
<Divider sx={{ my: 2 }} />
<ListSubheader>Clustering</ListSubheader>
<NumInput field="min_points" />
<UserTextInput field="min_points" />
<MultiOptionList
field="cluster_mode"
hideLabel
buttons={CLUSTERING_MODES}
buttons={clusterOptions}
type="select"
itemLabel={formatPluginName}
/>
<Collapse in={!fastest}>
<NumInput field="cluster_split_level" min={1} max={20} />
<UserTextInput field="cluster_split_level" min={1} max={20} />
</Collapse>
<Collapse in={!fastest}>
<NumInput field="max_clusters" min={0} />
<UserTextInput field="max_clusters" min={0} />
</Collapse>
<Collapse in={!CLUSTERING_MODES.some((m) => m === cluster_mode)}>
<UserTextInput field="clustering_args" helperText="--x 1 --y abc" />
</Collapse>
</Collapse>

<Divider sx={{ my: 2 }} />
<ListSubheader>Routing</ListSubheader>
<MultiOptionList field="sort_by" buttons={SORT_BY} type="select" />
<Collapse in={sort_by === 'TSP'}>
<NumInput field="route_split_level" min={1} max={12} />
<MultiOptionList
field="sort_by"
buttons={sortByOptions}
type="select"
itemLabel={formatPluginName}
/>
<Collapse in={!SORT_BY.some((sort) => sort === sort_by)}>
<UserTextInput field="route_split_level" min={1} max={12} />
</Collapse>
<Collapse
in={!SORT_BY.some((sort) => sort === sort_by) && sort_by !== 'tsp'}
>
<UserTextInput field="routing_args" helperText="--x 1 --y abc" />
</Collapse>

<Divider sx={{ my: 2 }} />
Expand Down
10 changes: 5 additions & 5 deletions client/src/components/drawer/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { fetchWrapper } from '@services/fetches'

import Toggle from './inputs/Toggle'
import ListSubheader from '../styled/Subheader'
import NumInput from './inputs/NumInput'
import UserTextInput from './inputs/NumInput'

export default function Settings() {
const navigate = useNavigate()
Expand Down Expand Up @@ -84,16 +84,16 @@ export default function Settings() {
)}
<Divider sx={{ my: 2 }} />
<ListSubheader disableGutters>Max Area to Auto Calc (km²)</ListSubheader>
<NumInput field="pokestopMaxAreaAutoCalc" label="Pokestops" />
<NumInput field="gymMaxAreaAutoCalc" label="Gyms" />
<NumInput field="spawnpointMaxAreaAutoCalc" label="Spawnpoints" />
<UserTextInput field="pokestopMaxAreaAutoCalc" label="Pokestops" />
<UserTextInput field="gymMaxAreaAutoCalc" label="Gyms" />
<UserTextInput field="spawnpointMaxAreaAutoCalc" label="Spawnpoints" />
{process.env.NODE_ENV === 'development' && (
<>
<Divider sx={{ my: 2 }} />
<ListSubheader disableGutters>Dev</ListSubheader>
<Toggle field="nativeLeaflet" />
<Toggle field="colorByGeohash" />
<NumInput field="geohashPrecision" />
<UserTextInput field="geohashPrecision" />
</>
)}
<Divider sx={{ my: 2 }} />
Expand Down
22 changes: 13 additions & 9 deletions client/src/components/drawer/inputs/MultiOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ interface Props<T extends FieldType, K extends UsePersist[T]> {
itemLabel?: (item: K) => string
}

const defaultLabel = (item: string | number) =>
typeof item === 'string'
? item.includes('_')
? fromSnakeCase(item)
: fromCamelCase(item)
: `${item}`

export default function MultiOptions<
T extends FieldType,
K extends UsePersist[T],
Expand All @@ -36,22 +43,17 @@ export default function MultiOptions<
type = 'button',
label = '',
hideLabel = !label,
itemLabel = (item: number | string) =>
typeof item === 'string'
? item.includes('_')
? fromSnakeCase(item)
: fromCamelCase(item)
: `${item}`,
itemLabel = defaultLabel,
}: Props<T, K>) {
const [value, setValue] = usePersist((s) => [s[field], usePersist.setState])
const value = usePersist((s) => s[field])

return type === 'button' ? (
<ToggleButtonGroup
size="small"
color="primary"
value={value}
exclusive
onChange={(_e, v) => setValue({ [field]: v })}
onChange={(_e, v) => usePersist.setState({ [field]: v })}
sx={{ mx: 'auto' }}
disabled={disabled}
>
Expand All @@ -72,7 +74,9 @@ export default function MultiOptions<
label={hideLabel ? undefined : label}
value={value}
color="primary"
onChange={({ target }) => setValue({ [field]: target.value as K })} // Mui y u like this
onChange={({ target }) =>
usePersist.setState({ [field]: target.value as K })
} // Mui y u like this
sx={{ mx: 'auto', minWidth: 150 }}
disabled={disabled}
>
Expand Down
43 changes: 24 additions & 19 deletions client/src/components/drawer/inputs/NumInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,47 +6,52 @@ import { fromCamelCase, fromSnakeCase } from '@services/utils'
import { UsePersist, usePersist } from '@hooks/usePersist'
import { OnlyType } from '@assets/types'

export default function NumInput<
T extends keyof Omit<
OnlyType<UsePersist, number | ''>,
's2_level' | 's2_size'
>,
export default function UserTextInput<
U extends UsePersist[T],
T extends keyof Omit<OnlyType<UsePersist, U | ''>, 's2_level' | 's2_size'>,
>({
field,
label,
helperText,
endAdornment,
disabled = false,
min = 0,
max = 9999,
min,
max,
}: {
field: T
label?: string
helperText?: string
disabled?: boolean
endAdornment?: string
min?: number
max?: number
min?: U extends number ? number : never
max?: U extends number ? number : never
}) {
const value = usePersist((s) => s[field])
const isNumber = typeof value === 'number'
const finalLabel =
label ?? (field.includes('_') ? fromSnakeCase(field) : fromCamelCase(field))

return (
<ListItem disabled={disabled}>
<ListItemText
primary={
label ??
(field.includes('_') ? fromSnakeCase(field) : fromCamelCase(field))
}
/>
{isNumber && <ListItemText primary={finalLabel} />}
<TextField
name={field}
value={value || ''}
type="number"
fullWidth
type={isNumber ? 'number' : 'text'}
size="small"
onChange={({ target }) =>
usePersist.setState({ [field]: +target.value })
usePersist.setState({
[field]: isNumber ? +target.value : target.value,
})
}
sx={{ width: '35%' }}
inputProps={{ min, max }}
label={isNumber ? undefined : finalLabel}
sx={{ width: isNumber ? '35%' : '100%' }}
multiline={!isNumber}
inputProps={{ min: min || 0, max: max || 9999 }}
InputProps={{ endAdornment }}
disabled={disabled}
helperText={helperText}
/>
</ListItem>
)
Expand Down
16 changes: 11 additions & 5 deletions client/src/hooks/usePersist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,11 @@ export interface UsePersist {

// Clustering
category: Category | 'fort'
cluster_mode: typeof CLUSTERING_MODES[number]
cluster_mode: typeof CLUSTERING_MODES[number] | string
tth: typeof TTH[number]
lineColorRules: { distance: number; color: string }[]
mode: typeof MODES[number]
sort_by: typeof SORT_BY[number]
sort_by: typeof SORT_BY[number] | string
radius: number | ''
min_points: number | ''
route_split_level: number | ''
Expand All @@ -68,10 +68,13 @@ export interface UsePersist {
save_to_scanner: boolean
skipRendering: boolean
fast: boolean
calculation_mode: typeof CALC_MODE[number]
calculation_mode: typeof CALC_MODE[number] | string
s2_level: typeof S2_CELL_LEVELS[number]
s2_size: typeof BOOTSTRAP_LEVELS[number]
max_clusters: number
routing_args: string
clustering_args: string
bootstrapping_args: string
// generations: number | ''
// routing_time: number | ''
// devices: number | ''
Expand Down Expand Up @@ -116,8 +119,8 @@ export const usePersist = create(
s2DisplayMode: 'none',
s2FillMode: 'simple',
radius: 70,
route_split_level: 1,
cluster_split_level: 10,
route_split_level: 0,
cluster_split_level: 0,
// routing_chunk_size: 0,
calculation_mode: 'Radius',
s2_level: 15,
Expand Down Expand Up @@ -153,6 +156,9 @@ export const usePersist = create(
pokestopMaxAreaAutoCalc: 100,
gymMaxAreaAutoCalc: 100,
spawnpointMaxAreaAutoCalc: 100,
routing_args: '',
clustering_args: '',
bootstrapping_args: '',
setStore: (key, value) => set({ [key]: value }),
}),
{
Expand Down
Loading