Skip to content

Commit

Permalink
Merge pull request #215 from TurtIeSocks/feat-plugins
Browse files Browse the repository at this point in the history
feat: Support for Plugins
  • Loading branch information
TurtIeSocks authored Mar 14, 2024
2 parents fbc30f7 + ea55f54 commit aeb6b06
Show file tree
Hide file tree
Showing 59 changed files with 1,372 additions and 594 deletions.
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
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ COPY --from=client /app/dist ./dist
COPY --from=server /usr/local/cargo/bin/koji /usr/local/bin/koji
RUN apt install curl -y
RUN apt install -y build-essential cmake lsb-release
RUN mkdir -p /algorithms/src/routing
RUN mkdir -p /algorithms/src/routing/plugins
COPY ./or-tools .
RUN curl -L https://github.com/google/or-tools/releases/download/v9.5/or-tools_amd64_debian-11_cpp_v9.5.2237.tar.gz -o ortools.tar.gz
RUN cat ortools.tar.gz | tar -xzf - && \
Expand All @@ -28,5 +28,5 @@ RUN cat ortools.tar.gz | tar -xzf - && \
cp /tsp/tsp.cc ./examples/koji/koji.cc && \
cp /tsp/CMakeLists.txt ./examples/koji/CMakeLists.txt && \
make build SOURCE=examples/koji/koji.cc && \
mv ./examples/koji/build/bin/koji /algorithms/src/routing/tsp
mv ./examples/koji/build/bin/koji /algorithms/src/routing/plugins/tsp
CMD koji
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

0 comments on commit aeb6b06

Please sign in to comment.